roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   T      CST        Timezone setting of the machine running the code
1011   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1012 </pre>
1013  *
1014  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1015  * <pre><code>
1016 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1017 document.write(dt.format('Y-m-d'));                         //2007-01-10
1018 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1019 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1020  </code></pre>
1021  *
1022  * Here are some standard date/time patterns that you might find helpful.  They
1023  * are not part of the source of Date.js, but to use them you can simply copy this
1024  * block of code into any script that is included after Date.js and they will also become
1025  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1026  * <pre><code>
1027 Date.patterns = {
1028     ISO8601Long:"Y-m-d H:i:s",
1029     ISO8601Short:"Y-m-d",
1030     ShortDate: "n/j/Y",
1031     LongDate: "l, F d, Y",
1032     FullDateTime: "l, F d, Y g:i:s A",
1033     MonthDay: "F d",
1034     ShortTime: "g:i A",
1035     LongTime: "g:i:s A",
1036     SortableDateTime: "Y-m-d\\TH:i:s",
1037     UniversalSortableDateTime: "Y-m-d H:i:sO",
1038     YearMonth: "F, Y"
1039 };
1040 </code></pre>
1041  *
1042  * Example usage:
1043  * <pre><code>
1044 var dt = new Date();
1045 document.write(dt.format(Date.patterns.ShortDate));
1046  </code></pre>
1047  */
1048
1049 /*
1050  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1051  * They generate precompiled functions from date formats instead of parsing and
1052  * processing the pattern every time you format a date.  These functions are available
1053  * on every Date object (any javascript function).
1054  *
1055  * The original article and download are here:
1056  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1057  *
1058  */
1059  
1060  
1061  // was in core
1062 /**
1063  Returns the number of milliseconds between this date and date
1064  @param {Date} date (optional) Defaults to now
1065  @return {Number} The diff in milliseconds
1066  @member Date getElapsed
1067  */
1068 Date.prototype.getElapsed = function(date) {
1069         return Math.abs((date || new Date()).getTime()-this.getTime());
1070 };
1071 // was in date file..
1072
1073
1074 // private
1075 Date.parseFunctions = {count:0};
1076 // private
1077 Date.parseRegexes = [];
1078 // private
1079 Date.formatFunctions = {count:0};
1080
1081 // private
1082 Date.prototype.dateFormat = function(format) {
1083     if (Date.formatFunctions[format] == null) {
1084         Date.createNewFormat(format);
1085     }
1086     var func = Date.formatFunctions[format];
1087     return this[func]();
1088 };
1089
1090
1091 /**
1092  * Formats a date given the supplied format string
1093  * @param {String} format The format string
1094  * @return {String} The formatted date
1095  * @method
1096  */
1097 Date.prototype.format = Date.prototype.dateFormat;
1098
1099 // private
1100 Date.createNewFormat = function(format) {
1101     var funcName = "format" + Date.formatFunctions.count++;
1102     Date.formatFunctions[format] = funcName;
1103     var code = "Date.prototype." + funcName + " = function(){return ";
1104     var special = false;
1105     var ch = '';
1106     for (var i = 0; i < format.length; ++i) {
1107         ch = format.charAt(i);
1108         if (!special && ch == "\\") {
1109             special = true;
1110         }
1111         else if (special) {
1112             special = false;
1113             code += "'" + String.escape(ch) + "' + ";
1114         }
1115         else {
1116             code += Date.getFormatCode(ch);
1117         }
1118     }
1119     /** eval:var:zzzzzzzzzzzzz */
1120     eval(code.substring(0, code.length - 3) + ";}");
1121 };
1122
1123 // private
1124 Date.getFormatCode = function(character) {
1125     switch (character) {
1126     case "d":
1127         return "String.leftPad(this.getDate(), 2, '0') + ";
1128     case "D":
1129         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1130     case "j":
1131         return "this.getDate() + ";
1132     case "l":
1133         return "Date.dayNames[this.getDay()] + ";
1134     case "S":
1135         return "this.getSuffix() + ";
1136     case "w":
1137         return "this.getDay() + ";
1138     case "z":
1139         return "this.getDayOfYear() + ";
1140     case "W":
1141         return "this.getWeekOfYear() + ";
1142     case "F":
1143         return "Date.monthNames[this.getMonth()] + ";
1144     case "m":
1145         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1146     case "M":
1147         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1148     case "n":
1149         return "(this.getMonth() + 1) + ";
1150     case "t":
1151         return "this.getDaysInMonth() + ";
1152     case "L":
1153         return "(this.isLeapYear() ? 1 : 0) + ";
1154     case "Y":
1155         return "this.getFullYear() + ";
1156     case "y":
1157         return "('' + this.getFullYear()).substring(2, 4) + ";
1158     case "a":
1159         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1160     case "A":
1161         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1162     case "g":
1163         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1164     case "G":
1165         return "this.getHours() + ";
1166     case "h":
1167         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1168     case "H":
1169         return "String.leftPad(this.getHours(), 2, '0') + ";
1170     case "i":
1171         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1172     case "s":
1173         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1174     case "O":
1175         return "this.getGMTOffset() + ";
1176     case "T":
1177         return "this.getTimezone() + ";
1178     case "Z":
1179         return "(this.getTimezoneOffset() * -60) + ";
1180     default:
1181         return "'" + String.escape(character) + "' + ";
1182     }
1183 };
1184
1185 /**
1186  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1187  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1188  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1189  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1190  * string or the parse operation will fail.
1191  * Example Usage:
1192 <pre><code>
1193 //dt = Fri May 25 2007 (current date)
1194 var dt = new Date();
1195
1196 //dt = Thu May 25 2006 (today's month/day in 2006)
1197 dt = Date.parseDate("2006", "Y");
1198
1199 //dt = Sun Jan 15 2006 (all date parts specified)
1200 dt = Date.parseDate("2006-1-15", "Y-m-d");
1201
1202 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1203 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1204 </code></pre>
1205  * @param {String} input The unparsed date as a string
1206  * @param {String} format The format the date is in
1207  * @return {Date} The parsed date
1208  * @static
1209  */
1210 Date.parseDate = function(input, format) {
1211     if (Date.parseFunctions[format] == null) {
1212         Date.createParser(format);
1213     }
1214     var func = Date.parseFunctions[format];
1215     return Date[func](input);
1216 };
1217 /**
1218  * @private
1219  */
1220 Date.createParser = function(format) {
1221     var funcName = "parse" + Date.parseFunctions.count++;
1222     var regexNum = Date.parseRegexes.length;
1223     var currentGroup = 1;
1224     Date.parseFunctions[format] = funcName;
1225
1226     var code = "Date." + funcName + " = function(input){\n"
1227         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1228         + "var d = new Date();\n"
1229         + "y = d.getFullYear();\n"
1230         + "m = d.getMonth();\n"
1231         + "d = d.getDate();\n"
1232         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1233         + "if (results && results.length > 0) {";
1234     var regex = "";
1235
1236     var special = false;
1237     var ch = '';
1238     for (var i = 0; i < format.length; ++i) {
1239         ch = format.charAt(i);
1240         if (!special && ch == "\\") {
1241             special = true;
1242         }
1243         else if (special) {
1244             special = false;
1245             regex += String.escape(ch);
1246         }
1247         else {
1248             var obj = Date.formatCodeToRegex(ch, currentGroup);
1249             currentGroup += obj.g;
1250             regex += obj.s;
1251             if (obj.g && obj.c) {
1252                 code += obj.c;
1253             }
1254         }
1255     }
1256
1257     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1258         + "{v = new Date(y, m, d, h, i, s);}\n"
1259         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1260         + "{v = new Date(y, m, d, h, i);}\n"
1261         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1262         + "{v = new Date(y, m, d, h);}\n"
1263         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1264         + "{v = new Date(y, m, d);}\n"
1265         + "else if (y >= 0 && m >= 0)\n"
1266         + "{v = new Date(y, m);}\n"
1267         + "else if (y >= 0)\n"
1268         + "{v = new Date(y);}\n"
1269         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1270         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1271         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1272         + ";}";
1273
1274     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1275     /** eval:var:zzzzzzzzzzzzz */
1276     eval(code);
1277 };
1278
1279 // private
1280 Date.formatCodeToRegex = function(character, currentGroup) {
1281     switch (character) {
1282     case "D":
1283         return {g:0,
1284         c:null,
1285         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1286     case "j":
1287         return {g:1,
1288             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1289             s:"(\\d{1,2})"}; // day of month without leading zeroes
1290     case "d":
1291         return {g:1,
1292             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1293             s:"(\\d{2})"}; // day of month with leading zeroes
1294     case "l":
1295         return {g:0,
1296             c:null,
1297             s:"(?:" + Date.dayNames.join("|") + ")"};
1298     case "S":
1299         return {g:0,
1300             c:null,
1301             s:"(?:st|nd|rd|th)"};
1302     case "w":
1303         return {g:0,
1304             c:null,
1305             s:"\\d"};
1306     case "z":
1307         return {g:0,
1308             c:null,
1309             s:"(?:\\d{1,3})"};
1310     case "W":
1311         return {g:0,
1312             c:null,
1313             s:"(?:\\d{2})"};
1314     case "F":
1315         return {g:1,
1316             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1317             s:"(" + Date.monthNames.join("|") + ")"};
1318     case "M":
1319         return {g:1,
1320             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1321             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1322     case "n":
1323         return {g:1,
1324             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1325             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1326     case "m":
1327         return {g:1,
1328             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1329             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1330     case "t":
1331         return {g:0,
1332             c:null,
1333             s:"\\d{1,2}"};
1334     case "L":
1335         return {g:0,
1336             c:null,
1337             s:"(?:1|0)"};
1338     case "Y":
1339         return {g:1,
1340             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1341             s:"(\\d{4})"};
1342     case "y":
1343         return {g:1,
1344             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1345                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1346             s:"(\\d{1,2})"};
1347     case "a":
1348         return {g:1,
1349             c:"if (results[" + currentGroup + "] == 'am') {\n"
1350                 + "if (h == 12) { h = 0; }\n"
1351                 + "} else { if (h < 12) { h += 12; }}",
1352             s:"(am|pm)"};
1353     case "A":
1354         return {g:1,
1355             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1356                 + "if (h == 12) { h = 0; }\n"
1357                 + "} else { if (h < 12) { h += 12; }}",
1358             s:"(AM|PM)"};
1359     case "g":
1360     case "G":
1361         return {g:1,
1362             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1363             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1364     case "h":
1365     case "H":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1369     case "i":
1370         return {g:1,
1371             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1372             s:"(\\d{2})"};
1373     case "s":
1374         return {g:1,
1375             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1376             s:"(\\d{2})"};
1377     case "O":
1378         return {g:1,
1379             c:[
1380                 "o = results[", currentGroup, "];\n",
1381                 "var sn = o.substring(0,1);\n", // get + / - sign
1382                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1383                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1384                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1385                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1386             ].join(""),
1387             s:"([+\-]\\d{4})"};
1388     case "T":
1389         return {g:0,
1390             c:null,
1391             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1392     case "Z":
1393         return {g:1,
1394             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1395                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1396             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1397     default:
1398         return {g:0,
1399             c:null,
1400             s:String.escape(character)};
1401     }
1402 };
1403
1404 /**
1405  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1406  * @return {String} The abbreviated timezone name (e.g. 'CST')
1407  */
1408 Date.prototype.getTimezone = function() {
1409     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1410 };
1411
1412 /**
1413  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1414  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1415  */
1416 Date.prototype.getGMTOffset = function() {
1417     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1418         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1419         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1420 };
1421
1422 /**
1423  * Get the numeric day number of the year, adjusted for leap year.
1424  * @return {Number} 0 through 364 (365 in leap years)
1425  */
1426 Date.prototype.getDayOfYear = function() {
1427     var num = 0;
1428     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1429     for (var i = 0; i < this.getMonth(); ++i) {
1430         num += Date.daysInMonth[i];
1431     }
1432     return num + this.getDate() - 1;
1433 };
1434
1435 /**
1436  * Get the string representation of the numeric week number of the year
1437  * (equivalent to the format specifier 'W').
1438  * @return {String} '00' through '52'
1439  */
1440 Date.prototype.getWeekOfYear = function() {
1441     // Skip to Thursday of this week
1442     var now = this.getDayOfYear() + (4 - this.getDay());
1443     // Find the first Thursday of the year
1444     var jan1 = new Date(this.getFullYear(), 0, 1);
1445     var then = (7 - jan1.getDay() + 4);
1446     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1447 };
1448
1449 /**
1450  * Whether or not the current date is in a leap year.
1451  * @return {Boolean} True if the current date is in a leap year, else false
1452  */
1453 Date.prototype.isLeapYear = function() {
1454     var year = this.getFullYear();
1455     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1456 };
1457
1458 /**
1459  * Get the first day of the current month, adjusted for leap year.  The returned value
1460  * is the numeric day index within the week (0-6) which can be used in conjunction with
1461  * the {@link #monthNames} array to retrieve the textual day name.
1462  * Example:
1463  *<pre><code>
1464 var dt = new Date('1/10/2007');
1465 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1466 </code></pre>
1467  * @return {Number} The day number (0-6)
1468  */
1469 Date.prototype.getFirstDayOfMonth = function() {
1470     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1471     return (day < 0) ? (day + 7) : day;
1472 };
1473
1474 /**
1475  * Get the last day of the current month, adjusted for leap year.  The returned value
1476  * is the numeric day index within the week (0-6) which can be used in conjunction with
1477  * the {@link #monthNames} array to retrieve the textual day name.
1478  * Example:
1479  *<pre><code>
1480 var dt = new Date('1/10/2007');
1481 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1482 </code></pre>
1483  * @return {Number} The day number (0-6)
1484  */
1485 Date.prototype.getLastDayOfMonth = function() {
1486     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1487     return (day < 0) ? (day + 7) : day;
1488 };
1489
1490
1491 /**
1492  * Get the first date of this date's month
1493  * @return {Date}
1494  */
1495 Date.prototype.getFirstDateOfMonth = function() {
1496     return new Date(this.getFullYear(), this.getMonth(), 1);
1497 };
1498
1499 /**
1500  * Get the last date of this date's month
1501  * @return {Date}
1502  */
1503 Date.prototype.getLastDateOfMonth = function() {
1504     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1505 };
1506 /**
1507  * Get the number of days in the current month, adjusted for leap year.
1508  * @return {Number} The number of days in the month
1509  */
1510 Date.prototype.getDaysInMonth = function() {
1511     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1512     return Date.daysInMonth[this.getMonth()];
1513 };
1514
1515 /**
1516  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1517  * @return {String} 'st, 'nd', 'rd' or 'th'
1518  */
1519 Date.prototype.getSuffix = function() {
1520     switch (this.getDate()) {
1521         case 1:
1522         case 21:
1523         case 31:
1524             return "st";
1525         case 2:
1526         case 22:
1527             return "nd";
1528         case 3:
1529         case 23:
1530             return "rd";
1531         default:
1532             return "th";
1533     }
1534 };
1535
1536 // private
1537 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1538
1539 /**
1540  * An array of textual month names.
1541  * Override these values for international dates, for example...
1542  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1543  * @type Array
1544  * @static
1545  */
1546 Date.monthNames =
1547    ["January",
1548     "February",
1549     "March",
1550     "April",
1551     "May",
1552     "June",
1553     "July",
1554     "August",
1555     "September",
1556     "October",
1557     "November",
1558     "December"];
1559
1560 /**
1561  * An array of textual day names.
1562  * Override these values for international dates, for example...
1563  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1564  * @type Array
1565  * @static
1566  */
1567 Date.dayNames =
1568    ["Sunday",
1569     "Monday",
1570     "Tuesday",
1571     "Wednesday",
1572     "Thursday",
1573     "Friday",
1574     "Saturday"];
1575
1576 // private
1577 Date.y2kYear = 50;
1578 // private
1579 Date.monthNumbers = {
1580     Jan:0,
1581     Feb:1,
1582     Mar:2,
1583     Apr:3,
1584     May:4,
1585     Jun:5,
1586     Jul:6,
1587     Aug:7,
1588     Sep:8,
1589     Oct:9,
1590     Nov:10,
1591     Dec:11};
1592
1593 /**
1594  * Creates and returns a new Date instance with the exact same date value as the called instance.
1595  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1596  * variable will also be changed.  When the intention is to create a new variable that will not
1597  * modify the original instance, you should create a clone.
1598  *
1599  * Example of correctly cloning a date:
1600  * <pre><code>
1601 //wrong way:
1602 var orig = new Date('10/1/2006');
1603 var copy = orig;
1604 copy.setDate(5);
1605 document.write(orig);  //returns 'Thu Oct 05 2006'!
1606
1607 //correct way:
1608 var orig = new Date('10/1/2006');
1609 var copy = orig.clone();
1610 copy.setDate(5);
1611 document.write(orig);  //returns 'Thu Oct 01 2006'
1612 </code></pre>
1613  * @return {Date} The new Date instance
1614  */
1615 Date.prototype.clone = function() {
1616         return new Date(this.getTime());
1617 };
1618
1619 /**
1620  * Clears any time information from this date
1621  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1622  @return {Date} this or the clone
1623  */
1624 Date.prototype.clearTime = function(clone){
1625     if(clone){
1626         return this.clone().clearTime();
1627     }
1628     this.setHours(0);
1629     this.setMinutes(0);
1630     this.setSeconds(0);
1631     this.setMilliseconds(0);
1632     return this;
1633 };
1634
1635 // private
1636 // safari setMonth is broken
1637 if(Roo.isSafari){
1638     Date.brokenSetMonth = Date.prototype.setMonth;
1639         Date.prototype.setMonth = function(num){
1640                 if(num <= -1){
1641                         var n = Math.ceil(-num);
1642                         var back_year = Math.ceil(n/12);
1643                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1644                         this.setFullYear(this.getFullYear() - back_year);
1645                         return Date.brokenSetMonth.call(this, month);
1646                 } else {
1647                         return Date.brokenSetMonth.apply(this, arguments);
1648                 }
1649         };
1650 }
1651
1652 /** Date interval constant 
1653 * @static 
1654 * @type String */
1655 Date.MILLI = "ms";
1656 /** Date interval constant 
1657 * @static 
1658 * @type String */
1659 Date.SECOND = "s";
1660 /** Date interval constant 
1661 * @static 
1662 * @type String */
1663 Date.MINUTE = "mi";
1664 /** Date interval constant 
1665 * @static 
1666 * @type String */
1667 Date.HOUR = "h";
1668 /** Date interval constant 
1669 * @static 
1670 * @type String */
1671 Date.DAY = "d";
1672 /** Date interval constant 
1673 * @static 
1674 * @type String */
1675 Date.MONTH = "mo";
1676 /** Date interval constant 
1677 * @static 
1678 * @type String */
1679 Date.YEAR = "y";
1680
1681 /**
1682  * Provides a convenient method of performing basic date arithmetic.  This method
1683  * does not modify the Date instance being called - it creates and returns
1684  * a new Date instance containing the resulting date value.
1685  *
1686  * Examples:
1687  * <pre><code>
1688 //Basic usage:
1689 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1690 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1691
1692 //Negative values will subtract correctly:
1693 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1694 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1695
1696 //You can even chain several calls together in one line!
1697 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1698 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1699  </code></pre>
1700  *
1701  * @param {String} interval   A valid date interval enum value
1702  * @param {Number} value      The amount to add to the current date
1703  * @return {Date} The new Date instance
1704  */
1705 Date.prototype.add = function(interval, value){
1706   var d = this.clone();
1707   if (!interval || value === 0) return d;
1708   switch(interval.toLowerCase()){
1709     case Date.MILLI:
1710       d.setMilliseconds(this.getMilliseconds() + value);
1711       break;
1712     case Date.SECOND:
1713       d.setSeconds(this.getSeconds() + value);
1714       break;
1715     case Date.MINUTE:
1716       d.setMinutes(this.getMinutes() + value);
1717       break;
1718     case Date.HOUR:
1719       d.setHours(this.getHours() + value);
1720       break;
1721     case Date.DAY:
1722       d.setDate(this.getDate() + value);
1723       break;
1724     case Date.MONTH:
1725       var day = this.getDate();
1726       if(day > 28){
1727           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1728       }
1729       d.setDate(day);
1730       d.setMonth(this.getMonth() + value);
1731       break;
1732     case Date.YEAR:
1733       d.setFullYear(this.getFullYear() + value);
1734       break;
1735   }
1736   return d;
1737 };/*
1738  * Based on:
1739  * Ext JS Library 1.1.1
1740  * Copyright(c) 2006-2007, Ext JS, LLC.
1741  *
1742  * Originally Released Under LGPL - original licence link has changed is not relivant.
1743  *
1744  * Fork - LGPL
1745  * <script type="text/javascript">
1746  */
1747
1748 Roo.lib.Dom = {
1749     getViewWidth : function(full) {
1750         return full ? this.getDocumentWidth() : this.getViewportWidth();
1751     },
1752
1753     getViewHeight : function(full) {
1754         return full ? this.getDocumentHeight() : this.getViewportHeight();
1755     },
1756
1757     getDocumentHeight: function() {
1758         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1759         return Math.max(scrollHeight, this.getViewportHeight());
1760     },
1761
1762     getDocumentWidth: function() {
1763         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1764         return Math.max(scrollWidth, this.getViewportWidth());
1765     },
1766
1767     getViewportHeight: function() {
1768         var height = self.innerHeight;
1769         var mode = document.compatMode;
1770
1771         if ((mode || Roo.isIE) && !Roo.isOpera) {
1772             height = (mode == "CSS1Compat") ?
1773                      document.documentElement.clientHeight :
1774                      document.body.clientHeight;
1775         }
1776
1777         return height;
1778     },
1779
1780     getViewportWidth: function() {
1781         var width = self.innerWidth;
1782         var mode = document.compatMode;
1783
1784         if (mode || Roo.isIE) {
1785             width = (mode == "CSS1Compat") ?
1786                     document.documentElement.clientWidth :
1787                     document.body.clientWidth;
1788         }
1789         return width;
1790     },
1791
1792     isAncestor : function(p, c) {
1793         p = Roo.getDom(p);
1794         c = Roo.getDom(c);
1795         if (!p || !c) {
1796             return false;
1797         }
1798
1799         if (p.contains && !Roo.isSafari) {
1800             return p.contains(c);
1801         } else if (p.compareDocumentPosition) {
1802             return !!(p.compareDocumentPosition(c) & 16);
1803         } else {
1804             var parent = c.parentNode;
1805             while (parent) {
1806                 if (parent == p) {
1807                     return true;
1808                 }
1809                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1810                     return false;
1811                 }
1812                 parent = parent.parentNode;
1813             }
1814             return false;
1815         }
1816     },
1817
1818     getRegion : function(el) {
1819         return Roo.lib.Region.getRegion(el);
1820     },
1821
1822     getY : function(el) {
1823         return this.getXY(el)[1];
1824     },
1825
1826     getX : function(el) {
1827         return this.getXY(el)[0];
1828     },
1829
1830     getXY : function(el) {
1831         var p, pe, b, scroll, bd = document.body;
1832         el = Roo.getDom(el);
1833         var fly = Roo.lib.AnimBase.fly;
1834         if (el.getBoundingClientRect) {
1835             b = el.getBoundingClientRect();
1836             scroll = fly(document).getScroll();
1837             return [b.left + scroll.left, b.top + scroll.top];
1838         }
1839         var x = 0, y = 0;
1840
1841         p = el;
1842
1843         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1844
1845         while (p) {
1846
1847             x += p.offsetLeft;
1848             y += p.offsetTop;
1849
1850             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1851                 hasAbsolute = true;
1852             }
1853
1854             if (Roo.isGecko) {
1855                 pe = fly(p);
1856
1857                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1858                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1859
1860
1861                 x += bl;
1862                 y += bt;
1863
1864
1865                 if (p != el && pe.getStyle('overflow') != 'visible') {
1866                     x += bl;
1867                     y += bt;
1868                 }
1869             }
1870             p = p.offsetParent;
1871         }
1872
1873         if (Roo.isSafari && hasAbsolute) {
1874             x -= bd.offsetLeft;
1875             y -= bd.offsetTop;
1876         }
1877
1878         if (Roo.isGecko && !hasAbsolute) {
1879             var dbd = fly(bd);
1880             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1881             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1882         }
1883
1884         p = el.parentNode;
1885         while (p && p != bd) {
1886             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1887                 x -= p.scrollLeft;
1888                 y -= p.scrollTop;
1889             }
1890             p = p.parentNode;
1891         }
1892         return [x, y];
1893     },
1894  
1895   
1896
1897
1898     setXY : function(el, xy) {
1899         el = Roo.fly(el, '_setXY');
1900         el.position();
1901         var pts = el.translatePoints(xy);
1902         if (xy[0] !== false) {
1903             el.dom.style.left = pts.left + "px";
1904         }
1905         if (xy[1] !== false) {
1906             el.dom.style.top = pts.top + "px";
1907         }
1908     },
1909
1910     setX : function(el, x) {
1911         this.setXY(el, [x, false]);
1912     },
1913
1914     setY : function(el, y) {
1915         this.setXY(el, [false, y]);
1916     }
1917 };
1918 /*
1919  * Portions of this file are based on pieces of Yahoo User Interface Library
1920  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1921  * YUI licensed under the BSD License:
1922  * http://developer.yahoo.net/yui/license.txt
1923  * <script type="text/javascript">
1924  *
1925  */
1926
1927 Roo.lib.Event = function() {
1928     var loadComplete = false;
1929     var listeners = [];
1930     var unloadListeners = [];
1931     var retryCount = 0;
1932     var onAvailStack = [];
1933     var counter = 0;
1934     var lastError = null;
1935
1936     return {
1937         POLL_RETRYS: 200,
1938         POLL_INTERVAL: 20,
1939         EL: 0,
1940         TYPE: 1,
1941         FN: 2,
1942         WFN: 3,
1943         OBJ: 3,
1944         ADJ_SCOPE: 4,
1945         _interval: null,
1946
1947         startInterval: function() {
1948             if (!this._interval) {
1949                 var self = this;
1950                 var callback = function() {
1951                     self._tryPreloadAttach();
1952                 };
1953                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1954
1955             }
1956         },
1957
1958         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1959             onAvailStack.push({ id:         p_id,
1960                 fn:         p_fn,
1961                 obj:        p_obj,
1962                 override:   p_override,
1963                 checkReady: false    });
1964
1965             retryCount = this.POLL_RETRYS;
1966             this.startInterval();
1967         },
1968
1969
1970         addListener: function(el, eventName, fn) {
1971             el = Roo.getDom(el);
1972             if (!el || !fn) {
1973                 return false;
1974             }
1975
1976             if ("unload" == eventName) {
1977                 unloadListeners[unloadListeners.length] =
1978                 [el, eventName, fn];
1979                 return true;
1980             }
1981
1982             var wrappedFn = function(e) {
1983                 return fn(Roo.lib.Event.getEvent(e));
1984             };
1985
1986             var li = [el, eventName, fn, wrappedFn];
1987
1988             var index = listeners.length;
1989             listeners[index] = li;
1990
1991             this.doAdd(el, eventName, wrappedFn, false);
1992             return true;
1993
1994         },
1995
1996
1997         removeListener: function(el, eventName, fn) {
1998             var i, len;
1999
2000             el = Roo.getDom(el);
2001
2002             if(!fn) {
2003                 return this.purgeElement(el, false, eventName);
2004             }
2005
2006
2007             if ("unload" == eventName) {
2008
2009                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2010                     var li = unloadListeners[i];
2011                     if (li &&
2012                         li[0] == el &&
2013                         li[1] == eventName &&
2014                         li[2] == fn) {
2015                         unloadListeners.splice(i, 1);
2016                         return true;
2017                     }
2018                 }
2019
2020                 return false;
2021             }
2022
2023             var cacheItem = null;
2024
2025
2026             var index = arguments[3];
2027
2028             if ("undefined" == typeof index) {
2029                 index = this._getCacheIndex(el, eventName, fn);
2030             }
2031
2032             if (index >= 0) {
2033                 cacheItem = listeners[index];
2034             }
2035
2036             if (!el || !cacheItem) {
2037                 return false;
2038             }
2039
2040             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2041
2042             delete listeners[index][this.WFN];
2043             delete listeners[index][this.FN];
2044             listeners.splice(index, 1);
2045
2046             return true;
2047
2048         },
2049
2050
2051         getTarget: function(ev, resolveTextNode) {
2052             ev = ev.browserEvent || ev;
2053             var t = ev.target || ev.srcElement;
2054             return this.resolveTextNode(t);
2055         },
2056
2057
2058         resolveTextNode: function(node) {
2059             if (Roo.isSafari && node && 3 == node.nodeType) {
2060                 return node.parentNode;
2061             } else {
2062                 return node;
2063             }
2064         },
2065
2066
2067         getPageX: function(ev) {
2068             ev = ev.browserEvent || ev;
2069             var x = ev.pageX;
2070             if (!x && 0 !== x) {
2071                 x = ev.clientX || 0;
2072
2073                 if (Roo.isIE) {
2074                     x += this.getScroll()[1];
2075                 }
2076             }
2077
2078             return x;
2079         },
2080
2081
2082         getPageY: function(ev) {
2083             ev = ev.browserEvent || ev;
2084             var y = ev.pageY;
2085             if (!y && 0 !== y) {
2086                 y = ev.clientY || 0;
2087
2088                 if (Roo.isIE) {
2089                     y += this.getScroll()[0];
2090                 }
2091             }
2092
2093
2094             return y;
2095         },
2096
2097
2098         getXY: function(ev) {
2099             ev = ev.browserEvent || ev;
2100             return [this.getPageX(ev), this.getPageY(ev)];
2101         },
2102
2103
2104         getRelatedTarget: function(ev) {
2105             ev = ev.browserEvent || ev;
2106             var t = ev.relatedTarget;
2107             if (!t) {
2108                 if (ev.type == "mouseout") {
2109                     t = ev.toElement;
2110                 } else if (ev.type == "mouseover") {
2111                     t = ev.fromElement;
2112                 }
2113             }
2114
2115             return this.resolveTextNode(t);
2116         },
2117
2118
2119         getTime: function(ev) {
2120             ev = ev.browserEvent || ev;
2121             if (!ev.time) {
2122                 var t = new Date().getTime();
2123                 try {
2124                     ev.time = t;
2125                 } catch(ex) {
2126                     this.lastError = ex;
2127                     return t;
2128                 }
2129             }
2130
2131             return ev.time;
2132         },
2133
2134
2135         stopEvent: function(ev) {
2136             this.stopPropagation(ev);
2137             this.preventDefault(ev);
2138         },
2139
2140
2141         stopPropagation: function(ev) {
2142             ev = ev.browserEvent || ev;
2143             if (ev.stopPropagation) {
2144                 ev.stopPropagation();
2145             } else {
2146                 ev.cancelBubble = true;
2147             }
2148         },
2149
2150
2151         preventDefault: function(ev) {
2152             ev = ev.browserEvent || ev;
2153             if(ev.preventDefault) {
2154                 ev.preventDefault();
2155             } else {
2156                 ev.returnValue = false;
2157             }
2158         },
2159
2160
2161         getEvent: function(e) {
2162             var ev = e || window.event;
2163             if (!ev) {
2164                 var c = this.getEvent.caller;
2165                 while (c) {
2166                     ev = c.arguments[0];
2167                     if (ev && Event == ev.constructor) {
2168                         break;
2169                     }
2170                     c = c.caller;
2171                 }
2172             }
2173             return ev;
2174         },
2175
2176
2177         getCharCode: function(ev) {
2178             ev = ev.browserEvent || ev;
2179             return ev.charCode || ev.keyCode || 0;
2180         },
2181
2182
2183         _getCacheIndex: function(el, eventName, fn) {
2184             for (var i = 0,len = listeners.length; i < len; ++i) {
2185                 var li = listeners[i];
2186                 if (li &&
2187                     li[this.FN] == fn &&
2188                     li[this.EL] == el &&
2189                     li[this.TYPE] == eventName) {
2190                     return i;
2191                 }
2192             }
2193
2194             return -1;
2195         },
2196
2197
2198         elCache: {},
2199
2200
2201         getEl: function(id) {
2202             return document.getElementById(id);
2203         },
2204
2205
2206         clearCache: function() {
2207         },
2208
2209
2210         _load: function(e) {
2211             loadComplete = true;
2212             var EU = Roo.lib.Event;
2213
2214
2215             if (Roo.isIE) {
2216                 EU.doRemove(window, "load", EU._load);
2217             }
2218         },
2219
2220
2221         _tryPreloadAttach: function() {
2222
2223             if (this.locked) {
2224                 return false;
2225             }
2226
2227             this.locked = true;
2228
2229
2230             var tryAgain = !loadComplete;
2231             if (!tryAgain) {
2232                 tryAgain = (retryCount > 0);
2233             }
2234
2235
2236             var notAvail = [];
2237             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2238                 var item = onAvailStack[i];
2239                 if (item) {
2240                     var el = this.getEl(item.id);
2241
2242                     if (el) {
2243                         if (!item.checkReady ||
2244                             loadComplete ||
2245                             el.nextSibling ||
2246                             (document && document.body)) {
2247
2248                             var scope = el;
2249                             if (item.override) {
2250                                 if (item.override === true) {
2251                                     scope = item.obj;
2252                                 } else {
2253                                     scope = item.override;
2254                                 }
2255                             }
2256                             item.fn.call(scope, item.obj);
2257                             onAvailStack[i] = null;
2258                         }
2259                     } else {
2260                         notAvail.push(item);
2261                     }
2262                 }
2263             }
2264
2265             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2266
2267             if (tryAgain) {
2268
2269                 this.startInterval();
2270             } else {
2271                 clearInterval(this._interval);
2272                 this._interval = null;
2273             }
2274
2275             this.locked = false;
2276
2277             return true;
2278
2279         },
2280
2281
2282         purgeElement: function(el, recurse, eventName) {
2283             var elListeners = this.getListeners(el, eventName);
2284             if (elListeners) {
2285                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2286                     var l = elListeners[i];
2287                     this.removeListener(el, l.type, l.fn);
2288                 }
2289             }
2290
2291             if (recurse && el && el.childNodes) {
2292                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2293                     this.purgeElement(el.childNodes[i], recurse, eventName);
2294                 }
2295             }
2296         },
2297
2298
2299         getListeners: function(el, eventName) {
2300             var results = [], searchLists;
2301             if (!eventName) {
2302                 searchLists = [listeners, unloadListeners];
2303             } else if (eventName == "unload") {
2304                 searchLists = [unloadListeners];
2305             } else {
2306                 searchLists = [listeners];
2307             }
2308
2309             for (var j = 0; j < searchLists.length; ++j) {
2310                 var searchList = searchLists[j];
2311                 if (searchList && searchList.length > 0) {
2312                     for (var i = 0,len = searchList.length; i < len; ++i) {
2313                         var l = searchList[i];
2314                         if (l && l[this.EL] === el &&
2315                             (!eventName || eventName === l[this.TYPE])) {
2316                             results.push({
2317                                 type:   l[this.TYPE],
2318                                 fn:     l[this.FN],
2319                                 obj:    l[this.OBJ],
2320                                 adjust: l[this.ADJ_SCOPE],
2321                                 index:  i
2322                             });
2323                         }
2324                     }
2325                 }
2326             }
2327
2328             return (results.length) ? results : null;
2329         },
2330
2331
2332         _unload: function(e) {
2333
2334             var EU = Roo.lib.Event, i, j, l, len, index;
2335
2336             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2337                 l = unloadListeners[i];
2338                 if (l) {
2339                     var scope = window;
2340                     if (l[EU.ADJ_SCOPE]) {
2341                         if (l[EU.ADJ_SCOPE] === true) {
2342                             scope = l[EU.OBJ];
2343                         } else {
2344                             scope = l[EU.ADJ_SCOPE];
2345                         }
2346                     }
2347                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2348                     unloadListeners[i] = null;
2349                     l = null;
2350                     scope = null;
2351                 }
2352             }
2353
2354             unloadListeners = null;
2355
2356             if (listeners && listeners.length > 0) {
2357                 j = listeners.length;
2358                 while (j) {
2359                     index = j - 1;
2360                     l = listeners[index];
2361                     if (l) {
2362                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2363                                 l[EU.FN], index);
2364                     }
2365                     j = j - 1;
2366                 }
2367                 l = null;
2368
2369                 EU.clearCache();
2370             }
2371
2372             EU.doRemove(window, "unload", EU._unload);
2373
2374         },
2375
2376
2377         getScroll: function() {
2378             var dd = document.documentElement, db = document.body;
2379             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2380                 return [dd.scrollTop, dd.scrollLeft];
2381             } else if (db) {
2382                 return [db.scrollTop, db.scrollLeft];
2383             } else {
2384                 return [0, 0];
2385             }
2386         },
2387
2388
2389         doAdd: function () {
2390             if (window.addEventListener) {
2391                 return function(el, eventName, fn, capture) {
2392                     el.addEventListener(eventName, fn, (capture));
2393                 };
2394             } else if (window.attachEvent) {
2395                 return function(el, eventName, fn, capture) {
2396                     el.attachEvent("on" + eventName, fn);
2397                 };
2398             } else {
2399                 return function() {
2400                 };
2401             }
2402         }(),
2403
2404
2405         doRemove: function() {
2406             if (window.removeEventListener) {
2407                 return function (el, eventName, fn, capture) {
2408                     el.removeEventListener(eventName, fn, (capture));
2409                 };
2410             } else if (window.detachEvent) {
2411                 return function (el, eventName, fn) {
2412                     el.detachEvent("on" + eventName, fn);
2413                 };
2414             } else {
2415                 return function() {
2416                 };
2417             }
2418         }()
2419     };
2420     
2421 }();
2422 (function() {     
2423    
2424     var E = Roo.lib.Event;
2425     E.on = E.addListener;
2426     E.un = E.removeListener;
2427
2428     if (document && document.body) {
2429         E._load();
2430     } else {
2431         E.doAdd(window, "load", E._load);
2432     }
2433     E.doAdd(window, "unload", E._unload);
2434     E._tryPreloadAttach();
2435 })();
2436
2437 /*
2438  * Portions of this file are based on pieces of Yahoo User Interface Library
2439  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2440  * YUI licensed under the BSD License:
2441  * http://developer.yahoo.net/yui/license.txt
2442  * <script type="text/javascript">
2443  *
2444  */
2445
2446 (function() {
2447     
2448     Roo.lib.Ajax = {
2449         request : function(method, uri, cb, data, options) {
2450             if(options){
2451                 var hs = options.headers;
2452                 if(hs){
2453                     for(var h in hs){
2454                         if(hs.hasOwnProperty(h)){
2455                             this.initHeader(h, hs[h], false);
2456                         }
2457                     }
2458                 }
2459                 if(options.xmlData){
2460                     this.initHeader('Content-Type', 'text/xml', false);
2461                     method = 'POST';
2462                     data = options.xmlData;
2463                 }
2464             }
2465
2466             return this.asyncRequest(method, uri, cb, data);
2467         },
2468
2469         serializeForm : function(form) {
2470             if(typeof form == 'string') {
2471                 form = (document.getElementById(form) || document.forms[form]);
2472             }
2473
2474             var el, name, val, disabled, data = '', hasSubmit = false;
2475             for (var i = 0; i < form.elements.length; i++) {
2476                 el = form.elements[i];
2477                 disabled = form.elements[i].disabled;
2478                 name = form.elements[i].name;
2479                 val = form.elements[i].value;
2480
2481                 if (!disabled && name){
2482                     switch (el.type)
2483                             {
2484                         case 'select-one':
2485                         case 'select-multiple':
2486                             for (var j = 0; j < el.options.length; j++) {
2487                                 if (el.options[j].selected) {
2488                                     if (Roo.isIE) {
2489                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2490                                     }
2491                                     else {
2492                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2493                                     }
2494                                 }
2495                             }
2496                             break;
2497                         case 'radio':
2498                         case 'checkbox':
2499                             if (el.checked) {
2500                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2501                             }
2502                             break;
2503                         case 'file':
2504
2505                         case undefined:
2506
2507                         case 'reset':
2508
2509                         case 'button':
2510
2511                             break;
2512                         case 'submit':
2513                             if(hasSubmit == false) {
2514                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2515                                 hasSubmit = true;
2516                             }
2517                             break;
2518                         default:
2519                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2520                             break;
2521                     }
2522                 }
2523             }
2524             data = data.substr(0, data.length - 1);
2525             return data;
2526         },
2527
2528         headers:{},
2529
2530         hasHeaders:false,
2531
2532         useDefaultHeader:true,
2533
2534         defaultPostHeader:'application/x-www-form-urlencoded',
2535
2536         useDefaultXhrHeader:true,
2537
2538         defaultXhrHeader:'XMLHttpRequest',
2539
2540         hasDefaultHeaders:true,
2541
2542         defaultHeaders:{},
2543
2544         poll:{},
2545
2546         timeout:{},
2547
2548         pollInterval:50,
2549
2550         transactionId:0,
2551
2552         setProgId:function(id)
2553         {
2554             this.activeX.unshift(id);
2555         },
2556
2557         setDefaultPostHeader:function(b)
2558         {
2559             this.useDefaultHeader = b;
2560         },
2561
2562         setDefaultXhrHeader:function(b)
2563         {
2564             this.useDefaultXhrHeader = b;
2565         },
2566
2567         setPollingInterval:function(i)
2568         {
2569             if (typeof i == 'number' && isFinite(i)) {
2570                 this.pollInterval = i;
2571             }
2572         },
2573
2574         createXhrObject:function(transactionId)
2575         {
2576             var obj,http;
2577             try
2578             {
2579
2580                 http = new XMLHttpRequest();
2581
2582                 obj = { conn:http, tId:transactionId };
2583             }
2584             catch(e)
2585             {
2586                 for (var i = 0; i < this.activeX.length; ++i) {
2587                     try
2588                     {
2589
2590                         http = new ActiveXObject(this.activeX[i]);
2591
2592                         obj = { conn:http, tId:transactionId };
2593                         break;
2594                     }
2595                     catch(e) {
2596                     }
2597                 }
2598             }
2599             finally
2600             {
2601                 return obj;
2602             }
2603         },
2604
2605         getConnectionObject:function()
2606         {
2607             var o;
2608             var tId = this.transactionId;
2609
2610             try
2611             {
2612                 o = this.createXhrObject(tId);
2613                 if (o) {
2614                     this.transactionId++;
2615                 }
2616             }
2617             catch(e) {
2618             }
2619             finally
2620             {
2621                 return o;
2622             }
2623         },
2624
2625         asyncRequest:function(method, uri, callback, postData)
2626         {
2627             var o = this.getConnectionObject();
2628
2629             if (!o) {
2630                 return null;
2631             }
2632             else {
2633                 o.conn.open(method, uri, true);
2634
2635                 if (this.useDefaultXhrHeader) {
2636                     if (!this.defaultHeaders['X-Requested-With']) {
2637                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2638                     }
2639                 }
2640
2641                 if(postData && this.useDefaultHeader){
2642                     this.initHeader('Content-Type', this.defaultPostHeader);
2643                 }
2644
2645                  if (this.hasDefaultHeaders || this.hasHeaders) {
2646                     this.setHeader(o);
2647                 }
2648
2649                 this.handleReadyState(o, callback);
2650                 o.conn.send(postData || null);
2651
2652                 return o;
2653             }
2654         },
2655
2656         handleReadyState:function(o, callback)
2657         {
2658             var oConn = this;
2659
2660             if (callback && callback.timeout) {
2661                 this.timeout[o.tId] = window.setTimeout(function() {
2662                     oConn.abort(o, callback, true);
2663                 }, callback.timeout);
2664             }
2665
2666             this.poll[o.tId] = window.setInterval(
2667                     function() {
2668                         if (o.conn && o.conn.readyState == 4) {
2669                             window.clearInterval(oConn.poll[o.tId]);
2670                             delete oConn.poll[o.tId];
2671
2672                             if(callback && callback.timeout) {
2673                                 window.clearTimeout(oConn.timeout[o.tId]);
2674                                 delete oConn.timeout[o.tId];
2675                             }
2676
2677                             oConn.handleTransactionResponse(o, callback);
2678                         }
2679                     }
2680                     , this.pollInterval);
2681         },
2682
2683         handleTransactionResponse:function(o, callback, isAbort)
2684         {
2685
2686             if (!callback) {
2687                 this.releaseObject(o);
2688                 return;
2689             }
2690
2691             var httpStatus, responseObject;
2692
2693             try
2694             {
2695                 if (o.conn.status !== undefined && o.conn.status != 0) {
2696                     httpStatus = o.conn.status;
2697                 }
2698                 else {
2699                     httpStatus = 13030;
2700                 }
2701             }
2702             catch(e) {
2703
2704
2705                 httpStatus = 13030;
2706             }
2707
2708             if (httpStatus >= 200 && httpStatus < 300) {
2709                 responseObject = this.createResponseObject(o, callback.argument);
2710                 if (callback.success) {
2711                     if (!callback.scope) {
2712                         callback.success(responseObject);
2713                     }
2714                     else {
2715
2716
2717                         callback.success.apply(callback.scope, [responseObject]);
2718                     }
2719                 }
2720             }
2721             else {
2722                 switch (httpStatus) {
2723
2724                     case 12002:
2725                     case 12029:
2726                     case 12030:
2727                     case 12031:
2728                     case 12152:
2729                     case 13030:
2730                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2731                         if (callback.failure) {
2732                             if (!callback.scope) {
2733                                 callback.failure(responseObject);
2734                             }
2735                             else {
2736                                 callback.failure.apply(callback.scope, [responseObject]);
2737                             }
2738                         }
2739                         break;
2740                     default:
2741                         responseObject = this.createResponseObject(o, callback.argument);
2742                         if (callback.failure) {
2743                             if (!callback.scope) {
2744                                 callback.failure(responseObject);
2745                             }
2746                             else {
2747                                 callback.failure.apply(callback.scope, [responseObject]);
2748                             }
2749                         }
2750                 }
2751             }
2752
2753             this.releaseObject(o);
2754             responseObject = null;
2755         },
2756
2757         createResponseObject:function(o, callbackArg)
2758         {
2759             var obj = {};
2760             var headerObj = {};
2761
2762             try
2763             {
2764                 var headerStr = o.conn.getAllResponseHeaders();
2765                 var header = headerStr.split('\n');
2766                 for (var i = 0; i < header.length; i++) {
2767                     var delimitPos = header[i].indexOf(':');
2768                     if (delimitPos != -1) {
2769                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2770                     }
2771                 }
2772             }
2773             catch(e) {
2774             }
2775
2776             obj.tId = o.tId;
2777             obj.status = o.conn.status;
2778             obj.statusText = o.conn.statusText;
2779             obj.getResponseHeader = headerObj;
2780             obj.getAllResponseHeaders = headerStr;
2781             obj.responseText = o.conn.responseText;
2782             obj.responseXML = o.conn.responseXML;
2783
2784             if (typeof callbackArg !== undefined) {
2785                 obj.argument = callbackArg;
2786             }
2787
2788             return obj;
2789         },
2790
2791         createExceptionObject:function(tId, callbackArg, isAbort)
2792         {
2793             var COMM_CODE = 0;
2794             var COMM_ERROR = 'communication failure';
2795             var ABORT_CODE = -1;
2796             var ABORT_ERROR = 'transaction aborted';
2797
2798             var obj = {};
2799
2800             obj.tId = tId;
2801             if (isAbort) {
2802                 obj.status = ABORT_CODE;
2803                 obj.statusText = ABORT_ERROR;
2804             }
2805             else {
2806                 obj.status = COMM_CODE;
2807                 obj.statusText = COMM_ERROR;
2808             }
2809
2810             if (callbackArg) {
2811                 obj.argument = callbackArg;
2812             }
2813
2814             return obj;
2815         },
2816
2817         initHeader:function(label, value, isDefault)
2818         {
2819             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2820
2821             if (headerObj[label] === undefined) {
2822                 headerObj[label] = value;
2823             }
2824             else {
2825
2826
2827                 headerObj[label] = value + "," + headerObj[label];
2828             }
2829
2830             if (isDefault) {
2831                 this.hasDefaultHeaders = true;
2832             }
2833             else {
2834                 this.hasHeaders = true;
2835             }
2836         },
2837
2838
2839         setHeader:function(o)
2840         {
2841             if (this.hasDefaultHeaders) {
2842                 for (var prop in this.defaultHeaders) {
2843                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2844                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2845                     }
2846                 }
2847             }
2848
2849             if (this.hasHeaders) {
2850                 for (var prop in this.headers) {
2851                     if (this.headers.hasOwnProperty(prop)) {
2852                         o.conn.setRequestHeader(prop, this.headers[prop]);
2853                     }
2854                 }
2855                 this.headers = {};
2856                 this.hasHeaders = false;
2857             }
2858         },
2859
2860         resetDefaultHeaders:function() {
2861             delete this.defaultHeaders;
2862             this.defaultHeaders = {};
2863             this.hasDefaultHeaders = false;
2864         },
2865
2866         abort:function(o, callback, isTimeout)
2867         {
2868             if(this.isCallInProgress(o)) {
2869                 o.conn.abort();
2870                 window.clearInterval(this.poll[o.tId]);
2871                 delete this.poll[o.tId];
2872                 if (isTimeout) {
2873                     delete this.timeout[o.tId];
2874                 }
2875
2876                 this.handleTransactionResponse(o, callback, true);
2877
2878                 return true;
2879             }
2880             else {
2881                 return false;
2882             }
2883         },
2884
2885
2886         isCallInProgress:function(o)
2887         {
2888             if (o && o.conn) {
2889                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2890             }
2891             else {
2892
2893                 return false;
2894             }
2895         },
2896
2897
2898         releaseObject:function(o)
2899         {
2900
2901             o.conn = null;
2902
2903             o = null;
2904         },
2905
2906         activeX:[
2907         'MSXML2.XMLHTTP.3.0',
2908         'MSXML2.XMLHTTP',
2909         'Microsoft.XMLHTTP'
2910         ]
2911
2912
2913     };
2914 })();/*
2915  * Portions of this file are based on pieces of Yahoo User Interface Library
2916  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2917  * YUI licensed under the BSD License:
2918  * http://developer.yahoo.net/yui/license.txt
2919  * <script type="text/javascript">
2920  *
2921  */
2922
2923 Roo.lib.Region = function(t, r, b, l) {
2924     this.top = t;
2925     this[1] = t;
2926     this.right = r;
2927     this.bottom = b;
2928     this.left = l;
2929     this[0] = l;
2930 };
2931
2932
2933 Roo.lib.Region.prototype = {
2934     contains : function(region) {
2935         return ( region.left >= this.left &&
2936                  region.right <= this.right &&
2937                  region.top >= this.top &&
2938                  region.bottom <= this.bottom    );
2939
2940     },
2941
2942     getArea : function() {
2943         return ( (this.bottom - this.top) * (this.right - this.left) );
2944     },
2945
2946     intersect : function(region) {
2947         var t = Math.max(this.top, region.top);
2948         var r = Math.min(this.right, region.right);
2949         var b = Math.min(this.bottom, region.bottom);
2950         var l = Math.max(this.left, region.left);
2951
2952         if (b >= t && r >= l) {
2953             return new Roo.lib.Region(t, r, b, l);
2954         } else {
2955             return null;
2956         }
2957     },
2958     union : function(region) {
2959         var t = Math.min(this.top, region.top);
2960         var r = Math.max(this.right, region.right);
2961         var b = Math.max(this.bottom, region.bottom);
2962         var l = Math.min(this.left, region.left);
2963
2964         return new Roo.lib.Region(t, r, b, l);
2965     },
2966
2967     adjust : function(t, l, b, r) {
2968         this.top += t;
2969         this.left += l;
2970         this.right += r;
2971         this.bottom += b;
2972         return this;
2973     }
2974 };
2975
2976 Roo.lib.Region.getRegion = function(el) {
2977     var p = Roo.lib.Dom.getXY(el);
2978
2979     var t = p[1];
2980     var r = p[0] + el.offsetWidth;
2981     var b = p[1] + el.offsetHeight;
2982     var l = p[0];
2983
2984     return new Roo.lib.Region(t, r, b, l);
2985 };
2986 /*
2987  * Portions of this file are based on pieces of Yahoo User Interface Library
2988  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2989  * YUI licensed under the BSD License:
2990  * http://developer.yahoo.net/yui/license.txt
2991  * <script type="text/javascript">
2992  *
2993  */
2994 //@@dep Roo.lib.Region
2995
2996
2997 Roo.lib.Point = function(x, y) {
2998     if (x instanceof Array) {
2999         y = x[1];
3000         x = x[0];
3001     }
3002     this.x = this.right = this.left = this[0] = x;
3003     this.y = this.top = this.bottom = this[1] = y;
3004 };
3005
3006 Roo.lib.Point.prototype = new Roo.lib.Region();
3007 /*
3008  * Portions of this file are based on pieces of Yahoo User Interface Library
3009  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3010  * YUI licensed under the BSD License:
3011  * http://developer.yahoo.net/yui/license.txt
3012  * <script type="text/javascript">
3013  *
3014  */
3015  
3016 (function() {   
3017
3018     Roo.lib.Anim = {
3019         scroll : function(el, args, duration, easing, cb, scope) {
3020             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3021         },
3022
3023         motion : function(el, args, duration, easing, cb, scope) {
3024             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3025         },
3026
3027         color : function(el, args, duration, easing, cb, scope) {
3028             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3029         },
3030
3031         run : function(el, args, duration, easing, cb, scope, type) {
3032             type = type || Roo.lib.AnimBase;
3033             if (typeof easing == "string") {
3034                 easing = Roo.lib.Easing[easing];
3035             }
3036             var anim = new type(el, args, duration, easing);
3037             anim.animateX(function() {
3038                 Roo.callback(cb, scope);
3039             });
3040             return anim;
3041         }
3042     };
3043 })();/*
3044  * Portions of this file are based on pieces of Yahoo User Interface Library
3045  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3046  * YUI licensed under the BSD License:
3047  * http://developer.yahoo.net/yui/license.txt
3048  * <script type="text/javascript">
3049  *
3050  */
3051
3052 (function() {    
3053     var libFlyweight;
3054     
3055     function fly(el) {
3056         if (!libFlyweight) {
3057             libFlyweight = new Roo.Element.Flyweight();
3058         }
3059         libFlyweight.dom = el;
3060         return libFlyweight;
3061     }
3062
3063     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3064     
3065    
3066     
3067     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3068         if (el) {
3069             this.init(el, attributes, duration, method);
3070         }
3071     };
3072
3073     Roo.lib.AnimBase.fly = fly;
3074     
3075     
3076     
3077     Roo.lib.AnimBase.prototype = {
3078
3079         toString: function() {
3080             var el = this.getEl();
3081             var id = el.id || el.tagName;
3082             return ("Anim " + id);
3083         },
3084
3085         patterns: {
3086             noNegatives:        /width|height|opacity|padding/i,
3087             offsetAttribute:  /^((width|height)|(top|left))$/,
3088             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3089             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3090         },
3091
3092
3093         doMethod: function(attr, start, end) {
3094             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3095         },
3096
3097
3098         setAttribute: function(attr, val, unit) {
3099             if (this.patterns.noNegatives.test(attr)) {
3100                 val = (val > 0) ? val : 0;
3101             }
3102
3103             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3104         },
3105
3106
3107         getAttribute: function(attr) {
3108             var el = this.getEl();
3109             var val = fly(el).getStyle(attr);
3110
3111             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3112                 return parseFloat(val);
3113             }
3114
3115             var a = this.patterns.offsetAttribute.exec(attr) || [];
3116             var pos = !!( a[3] );
3117             var box = !!( a[2] );
3118
3119
3120             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3121                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3122             } else {
3123                 val = 0;
3124             }
3125
3126             return val;
3127         },
3128
3129
3130         getDefaultUnit: function(attr) {
3131             if (this.patterns.defaultUnit.test(attr)) {
3132                 return 'px';
3133             }
3134
3135             return '';
3136         },
3137
3138         animateX : function(callback, scope) {
3139             var f = function() {
3140                 this.onComplete.removeListener(f);
3141                 if (typeof callback == "function") {
3142                     callback.call(scope || this, this);
3143                 }
3144             };
3145             this.onComplete.addListener(f, this);
3146             this.animate();
3147         },
3148
3149
3150         setRuntimeAttribute: function(attr) {
3151             var start;
3152             var end;
3153             var attributes = this.attributes;
3154
3155             this.runtimeAttributes[attr] = {};
3156
3157             var isset = function(prop) {
3158                 return (typeof prop !== 'undefined');
3159             };
3160
3161             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3162                 return false;
3163             }
3164
3165             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3166
3167
3168             if (isset(attributes[attr]['to'])) {
3169                 end = attributes[attr]['to'];
3170             } else if (isset(attributes[attr]['by'])) {
3171                 if (start.constructor == Array) {
3172                     end = [];
3173                     for (var i = 0, len = start.length; i < len; ++i) {
3174                         end[i] = start[i] + attributes[attr]['by'][i];
3175                     }
3176                 } else {
3177                     end = start + attributes[attr]['by'];
3178                 }
3179             }
3180
3181             this.runtimeAttributes[attr].start = start;
3182             this.runtimeAttributes[attr].end = end;
3183
3184
3185             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3186         },
3187
3188
3189         init: function(el, attributes, duration, method) {
3190
3191             var isAnimated = false;
3192
3193
3194             var startTime = null;
3195
3196
3197             var actualFrames = 0;
3198
3199
3200             el = Roo.getDom(el);
3201
3202
3203             this.attributes = attributes || {};
3204
3205
3206             this.duration = duration || 1;
3207
3208
3209             this.method = method || Roo.lib.Easing.easeNone;
3210
3211
3212             this.useSeconds = true;
3213
3214
3215             this.currentFrame = 0;
3216
3217
3218             this.totalFrames = Roo.lib.AnimMgr.fps;
3219
3220
3221             this.getEl = function() {
3222                 return el;
3223             };
3224
3225
3226             this.isAnimated = function() {
3227                 return isAnimated;
3228             };
3229
3230
3231             this.getStartTime = function() {
3232                 return startTime;
3233             };
3234
3235             this.runtimeAttributes = {};
3236
3237
3238             this.animate = function() {
3239                 if (this.isAnimated()) {
3240                     return false;
3241                 }
3242
3243                 this.currentFrame = 0;
3244
3245                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3246
3247                 Roo.lib.AnimMgr.registerElement(this);
3248             };
3249
3250
3251             this.stop = function(finish) {
3252                 if (finish) {
3253                     this.currentFrame = this.totalFrames;
3254                     this._onTween.fire();
3255                 }
3256                 Roo.lib.AnimMgr.stop(this);
3257             };
3258
3259             var onStart = function() {
3260                 this.onStart.fire();
3261
3262                 this.runtimeAttributes = {};
3263                 for (var attr in this.attributes) {
3264                     this.setRuntimeAttribute(attr);
3265                 }
3266
3267                 isAnimated = true;
3268                 actualFrames = 0;
3269                 startTime = new Date();
3270             };
3271
3272
3273             var onTween = function() {
3274                 var data = {
3275                     duration: new Date() - this.getStartTime(),
3276                     currentFrame: this.currentFrame
3277                 };
3278
3279                 data.toString = function() {
3280                     return (
3281                             'duration: ' + data.duration +
3282                             ', currentFrame: ' + data.currentFrame
3283                             );
3284                 };
3285
3286                 this.onTween.fire(data);
3287
3288                 var runtimeAttributes = this.runtimeAttributes;
3289
3290                 for (var attr in runtimeAttributes) {
3291                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3292                 }
3293
3294                 actualFrames += 1;
3295             };
3296
3297             var onComplete = function() {
3298                 var actual_duration = (new Date() - startTime) / 1000 ;
3299
3300                 var data = {
3301                     duration: actual_duration,
3302                     frames: actualFrames,
3303                     fps: actualFrames / actual_duration
3304                 };
3305
3306                 data.toString = function() {
3307                     return (
3308                             'duration: ' + data.duration +
3309                             ', frames: ' + data.frames +
3310                             ', fps: ' + data.fps
3311                             );
3312                 };
3313
3314                 isAnimated = false;
3315                 actualFrames = 0;
3316                 this.onComplete.fire(data);
3317             };
3318
3319
3320             this._onStart = new Roo.util.Event(this);
3321             this.onStart = new Roo.util.Event(this);
3322             this.onTween = new Roo.util.Event(this);
3323             this._onTween = new Roo.util.Event(this);
3324             this.onComplete = new Roo.util.Event(this);
3325             this._onComplete = new Roo.util.Event(this);
3326             this._onStart.addListener(onStart);
3327             this._onTween.addListener(onTween);
3328             this._onComplete.addListener(onComplete);
3329         }
3330     };
3331 })();
3332 /*
3333  * Portions of this file are based on pieces of Yahoo User Interface Library
3334  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3335  * YUI licensed under the BSD License:
3336  * http://developer.yahoo.net/yui/license.txt
3337  * <script type="text/javascript">
3338  *
3339  */
3340
3341 Roo.lib.AnimMgr = new function() {
3342
3343         var thread = null;
3344
3345
3346         var queue = [];
3347
3348
3349         var tweenCount = 0;
3350
3351
3352         this.fps = 1000;
3353
3354
3355         this.delay = 1;
3356
3357
3358         this.registerElement = function(tween) {
3359             queue[queue.length] = tween;
3360             tweenCount += 1;
3361             tween._onStart.fire();
3362             this.start();
3363         };
3364
3365
3366         this.unRegister = function(tween, index) {
3367             tween._onComplete.fire();
3368             index = index || getIndex(tween);
3369             if (index != -1) {
3370                 queue.splice(index, 1);
3371             }
3372
3373             tweenCount -= 1;
3374             if (tweenCount <= 0) {
3375                 this.stop();
3376             }
3377         };
3378
3379
3380         this.start = function() {
3381             if (thread === null) {
3382                 thread = setInterval(this.run, this.delay);
3383             }
3384         };
3385
3386
3387         this.stop = function(tween) {
3388             if (!tween) {
3389                 clearInterval(thread);
3390
3391                 for (var i = 0, len = queue.length; i < len; ++i) {
3392                     if (queue[0].isAnimated()) {
3393                         this.unRegister(queue[0], 0);
3394                     }
3395                 }
3396
3397                 queue = [];
3398                 thread = null;
3399                 tweenCount = 0;
3400             }
3401             else {
3402                 this.unRegister(tween);
3403             }
3404         };
3405
3406
3407         this.run = function() {
3408             for (var i = 0, len = queue.length; i < len; ++i) {
3409                 var tween = queue[i];
3410                 if (!tween || !tween.isAnimated()) {
3411                     continue;
3412                 }
3413
3414                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3415                 {
3416                     tween.currentFrame += 1;
3417
3418                     if (tween.useSeconds) {
3419                         correctFrame(tween);
3420                     }
3421                     tween._onTween.fire();
3422                 }
3423                 else {
3424                     Roo.lib.AnimMgr.stop(tween, i);
3425                 }
3426             }
3427         };
3428
3429         var getIndex = function(anim) {
3430             for (var i = 0, len = queue.length; i < len; ++i) {
3431                 if (queue[i] == anim) {
3432                     return i;
3433                 }
3434             }
3435             return -1;
3436         };
3437
3438
3439         var correctFrame = function(tween) {
3440             var frames = tween.totalFrames;
3441             var frame = tween.currentFrame;
3442             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3443             var elapsed = (new Date() - tween.getStartTime());
3444             var tweak = 0;
3445
3446             if (elapsed < tween.duration * 1000) {
3447                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3448             } else {
3449                 tweak = frames - (frame + 1);
3450             }
3451             if (tweak > 0 && isFinite(tweak)) {
3452                 if (tween.currentFrame + tweak >= frames) {
3453                     tweak = frames - (frame + 1);
3454                 }
3455
3456                 tween.currentFrame += tweak;
3457             }
3458         };
3459     };/*
3460  * Portions of this file are based on pieces of Yahoo User Interface Library
3461  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3462  * YUI licensed under the BSD License:
3463  * http://developer.yahoo.net/yui/license.txt
3464  * <script type="text/javascript">
3465  *
3466  */
3467 Roo.lib.Bezier = new function() {
3468
3469         this.getPosition = function(points, t) {
3470             var n = points.length;
3471             var tmp = [];
3472
3473             for (var i = 0; i < n; ++i) {
3474                 tmp[i] = [points[i][0], points[i][1]];
3475             }
3476
3477             for (var j = 1; j < n; ++j) {
3478                 for (i = 0; i < n - j; ++i) {
3479                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3480                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3481                 }
3482             }
3483
3484             return [ tmp[0][0], tmp[0][1] ];
3485
3486         };
3487     };/*
3488  * Portions of this file are based on pieces of Yahoo User Interface Library
3489  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3490  * YUI licensed under the BSD License:
3491  * http://developer.yahoo.net/yui/license.txt
3492  * <script type="text/javascript">
3493  *
3494  */
3495 (function() {
3496
3497     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3498         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3499     };
3500
3501     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3502
3503     var fly = Roo.lib.AnimBase.fly;
3504     var Y = Roo.lib;
3505     var superclass = Y.ColorAnim.superclass;
3506     var proto = Y.ColorAnim.prototype;
3507
3508     proto.toString = function() {
3509         var el = this.getEl();
3510         var id = el.id || el.tagName;
3511         return ("ColorAnim " + id);
3512     };
3513
3514     proto.patterns.color = /color$/i;
3515     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3516     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3517     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3518     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3519
3520
3521     proto.parseColor = function(s) {
3522         if (s.length == 3) {
3523             return s;
3524         }
3525
3526         var c = this.patterns.hex.exec(s);
3527         if (c && c.length == 4) {
3528             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3529         }
3530
3531         c = this.patterns.rgb.exec(s);
3532         if (c && c.length == 4) {
3533             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3534         }
3535
3536         c = this.patterns.hex3.exec(s);
3537         if (c && c.length == 4) {
3538             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3539         }
3540
3541         return null;
3542     };
3543     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3544     proto.getAttribute = function(attr) {
3545         var el = this.getEl();
3546         if (this.patterns.color.test(attr)) {
3547             var val = fly(el).getStyle(attr);
3548
3549             if (this.patterns.transparent.test(val)) {
3550                 var parent = el.parentNode;
3551                 val = fly(parent).getStyle(attr);
3552
3553                 while (parent && this.patterns.transparent.test(val)) {
3554                     parent = parent.parentNode;
3555                     val = fly(parent).getStyle(attr);
3556                     if (parent.tagName.toUpperCase() == 'HTML') {
3557                         val = '#fff';
3558                     }
3559                 }
3560             }
3561         } else {
3562             val = superclass.getAttribute.call(this, attr);
3563         }
3564
3565         return val;
3566     };
3567     proto.getAttribute = function(attr) {
3568         var el = this.getEl();
3569         if (this.patterns.color.test(attr)) {
3570             var val = fly(el).getStyle(attr);
3571
3572             if (this.patterns.transparent.test(val)) {
3573                 var parent = el.parentNode;
3574                 val = fly(parent).getStyle(attr);
3575
3576                 while (parent && this.patterns.transparent.test(val)) {
3577                     parent = parent.parentNode;
3578                     val = fly(parent).getStyle(attr);
3579                     if (parent.tagName.toUpperCase() == 'HTML') {
3580                         val = '#fff';
3581                     }
3582                 }
3583             }
3584         } else {
3585             val = superclass.getAttribute.call(this, attr);
3586         }
3587
3588         return val;
3589     };
3590
3591     proto.doMethod = function(attr, start, end) {
3592         var val;
3593
3594         if (this.patterns.color.test(attr)) {
3595             val = [];
3596             for (var i = 0, len = start.length; i < len; ++i) {
3597                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3598             }
3599
3600             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3601         }
3602         else {
3603             val = superclass.doMethod.call(this, attr, start, end);
3604         }
3605
3606         return val;
3607     };
3608
3609     proto.setRuntimeAttribute = function(attr) {
3610         superclass.setRuntimeAttribute.call(this, attr);
3611
3612         if (this.patterns.color.test(attr)) {
3613             var attributes = this.attributes;
3614             var start = this.parseColor(this.runtimeAttributes[attr].start);
3615             var end = this.parseColor(this.runtimeAttributes[attr].end);
3616
3617             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3618                 end = this.parseColor(attributes[attr].by);
3619
3620                 for (var i = 0, len = start.length; i < len; ++i) {
3621                     end[i] = start[i] + end[i];
3622                 }
3623             }
3624
3625             this.runtimeAttributes[attr].start = start;
3626             this.runtimeAttributes[attr].end = end;
3627         }
3628     };
3629 })();
3630
3631 /*
3632  * Portions of this file are based on pieces of Yahoo User Interface Library
3633  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3634  * YUI licensed under the BSD License:
3635  * http://developer.yahoo.net/yui/license.txt
3636  * <script type="text/javascript">
3637  *
3638  */
3639 Roo.lib.Easing = {
3640
3641
3642     easeNone: function (t, b, c, d) {
3643         return c * t / d + b;
3644     },
3645
3646
3647     easeIn: function (t, b, c, d) {
3648         return c * (t /= d) * t + b;
3649     },
3650
3651
3652     easeOut: function (t, b, c, d) {
3653         return -c * (t /= d) * (t - 2) + b;
3654     },
3655
3656
3657     easeBoth: function (t, b, c, d) {
3658         if ((t /= d / 2) < 1) {
3659             return c / 2 * t * t + b;
3660         }
3661
3662         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3663     },
3664
3665
3666     easeInStrong: function (t, b, c, d) {
3667         return c * (t /= d) * t * t * t + b;
3668     },
3669
3670
3671     easeOutStrong: function (t, b, c, d) {
3672         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3673     },
3674
3675
3676     easeBothStrong: function (t, b, c, d) {
3677         if ((t /= d / 2) < 1) {
3678             return c / 2 * t * t * t * t + b;
3679         }
3680
3681         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3682     },
3683
3684
3685
3686     elasticIn: function (t, b, c, d, a, p) {
3687         if (t == 0) {
3688             return b;
3689         }
3690         if ((t /= d) == 1) {
3691             return b + c;
3692         }
3693         if (!p) {
3694             p = d * .3;
3695         }
3696
3697         if (!a || a < Math.abs(c)) {
3698             a = c;
3699             var s = p / 4;
3700         }
3701         else {
3702             var s = p / (2 * Math.PI) * Math.asin(c / a);
3703         }
3704
3705         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3706     },
3707
3708
3709     elasticOut: function (t, b, c, d, a, p) {
3710         if (t == 0) {
3711             return b;
3712         }
3713         if ((t /= d) == 1) {
3714             return b + c;
3715         }
3716         if (!p) {
3717             p = d * .3;
3718         }
3719
3720         if (!a || a < Math.abs(c)) {
3721             a = c;
3722             var s = p / 4;
3723         }
3724         else {
3725             var s = p / (2 * Math.PI) * Math.asin(c / a);
3726         }
3727
3728         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3729     },
3730
3731
3732     elasticBoth: function (t, b, c, d, a, p) {
3733         if (t == 0) {
3734             return b;
3735         }
3736
3737         if ((t /= d / 2) == 2) {
3738             return b + c;
3739         }
3740
3741         if (!p) {
3742             p = d * (.3 * 1.5);
3743         }
3744
3745         if (!a || a < Math.abs(c)) {
3746             a = c;
3747             var s = p / 4;
3748         }
3749         else {
3750             var s = p / (2 * Math.PI) * Math.asin(c / a);
3751         }
3752
3753         if (t < 1) {
3754             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3755                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3756         }
3757         return a * Math.pow(2, -10 * (t -= 1)) *
3758                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3759     },
3760
3761
3762
3763     backIn: function (t, b, c, d, s) {
3764         if (typeof s == 'undefined') {
3765             s = 1.70158;
3766         }
3767         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3768     },
3769
3770
3771     backOut: function (t, b, c, d, s) {
3772         if (typeof s == 'undefined') {
3773             s = 1.70158;
3774         }
3775         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3776     },
3777
3778
3779     backBoth: function (t, b, c, d, s) {
3780         if (typeof s == 'undefined') {
3781             s = 1.70158;
3782         }
3783
3784         if ((t /= d / 2 ) < 1) {
3785             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3786         }
3787         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3788     },
3789
3790
3791     bounceIn: function (t, b, c, d) {
3792         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3793     },
3794
3795
3796     bounceOut: function (t, b, c, d) {
3797         if ((t /= d) < (1 / 2.75)) {
3798             return c * (7.5625 * t * t) + b;
3799         } else if (t < (2 / 2.75)) {
3800             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3801         } else if (t < (2.5 / 2.75)) {
3802             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3803         }
3804         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3805     },
3806
3807
3808     bounceBoth: function (t, b, c, d) {
3809         if (t < d / 2) {
3810             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3811         }
3812         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3813     }
3814 };/*
3815  * Portions of this file are based on pieces of Yahoo User Interface Library
3816  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3817  * YUI licensed under the BSD License:
3818  * http://developer.yahoo.net/yui/license.txt
3819  * <script type="text/javascript">
3820  *
3821  */
3822     (function() {
3823         Roo.lib.Motion = function(el, attributes, duration, method) {
3824             if (el) {
3825                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3826             }
3827         };
3828
3829         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3830
3831
3832         var Y = Roo.lib;
3833         var superclass = Y.Motion.superclass;
3834         var proto = Y.Motion.prototype;
3835
3836         proto.toString = function() {
3837             var el = this.getEl();
3838             var id = el.id || el.tagName;
3839             return ("Motion " + id);
3840         };
3841
3842         proto.patterns.points = /^points$/i;
3843
3844         proto.setAttribute = function(attr, val, unit) {
3845             if (this.patterns.points.test(attr)) {
3846                 unit = unit || 'px';
3847                 superclass.setAttribute.call(this, 'left', val[0], unit);
3848                 superclass.setAttribute.call(this, 'top', val[1], unit);
3849             } else {
3850                 superclass.setAttribute.call(this, attr, val, unit);
3851             }
3852         };
3853
3854         proto.getAttribute = function(attr) {
3855             if (this.patterns.points.test(attr)) {
3856                 var val = [
3857                         superclass.getAttribute.call(this, 'left'),
3858                         superclass.getAttribute.call(this, 'top')
3859                         ];
3860             } else {
3861                 val = superclass.getAttribute.call(this, attr);
3862             }
3863
3864             return val;
3865         };
3866
3867         proto.doMethod = function(attr, start, end) {
3868             var val = null;
3869
3870             if (this.patterns.points.test(attr)) {
3871                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3872                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3873             } else {
3874                 val = superclass.doMethod.call(this, attr, start, end);
3875             }
3876             return val;
3877         };
3878
3879         proto.setRuntimeAttribute = function(attr) {
3880             if (this.patterns.points.test(attr)) {
3881                 var el = this.getEl();
3882                 var attributes = this.attributes;
3883                 var start;
3884                 var control = attributes['points']['control'] || [];
3885                 var end;
3886                 var i, len;
3887
3888                 if (control.length > 0 && !(control[0] instanceof Array)) {
3889                     control = [control];
3890                 } else {
3891                     var tmp = [];
3892                     for (i = 0,len = control.length; i < len; ++i) {
3893                         tmp[i] = control[i];
3894                     }
3895                     control = tmp;
3896                 }
3897
3898                 Roo.fly(el).position();
3899
3900                 if (isset(attributes['points']['from'])) {
3901                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3902                 }
3903                 else {
3904                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3905                 }
3906
3907                 start = this.getAttribute('points');
3908
3909
3910                 if (isset(attributes['points']['to'])) {
3911                     end = translateValues.call(this, attributes['points']['to'], start);
3912
3913                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3914                     for (i = 0,len = control.length; i < len; ++i) {
3915                         control[i] = translateValues.call(this, control[i], start);
3916                     }
3917
3918
3919                 } else if (isset(attributes['points']['by'])) {
3920                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3921
3922                     for (i = 0,len = control.length; i < len; ++i) {
3923                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3924                     }
3925                 }
3926
3927                 this.runtimeAttributes[attr] = [start];
3928
3929                 if (control.length > 0) {
3930                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3931                 }
3932
3933                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3934             }
3935             else {
3936                 superclass.setRuntimeAttribute.call(this, attr);
3937             }
3938         };
3939
3940         var translateValues = function(val, start) {
3941             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3942             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3943
3944             return val;
3945         };
3946
3947         var isset = function(prop) {
3948             return (typeof prop !== 'undefined');
3949         };
3950     })();
3951 /*
3952  * Portions of this file are based on pieces of Yahoo User Interface Library
3953  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3954  * YUI licensed under the BSD License:
3955  * http://developer.yahoo.net/yui/license.txt
3956  * <script type="text/javascript">
3957  *
3958  */
3959     (function() {
3960         Roo.lib.Scroll = function(el, attributes, duration, method) {
3961             if (el) {
3962                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3963             }
3964         };
3965
3966         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3967
3968
3969         var Y = Roo.lib;
3970         var superclass = Y.Scroll.superclass;
3971         var proto = Y.Scroll.prototype;
3972
3973         proto.toString = function() {
3974             var el = this.getEl();
3975             var id = el.id || el.tagName;
3976             return ("Scroll " + id);
3977         };
3978
3979         proto.doMethod = function(attr, start, end) {
3980             var val = null;
3981
3982             if (attr == 'scroll') {
3983                 val = [
3984                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3985                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3986                         ];
3987
3988             } else {
3989                 val = superclass.doMethod.call(this, attr, start, end);
3990             }
3991             return val;
3992         };
3993
3994         proto.getAttribute = function(attr) {
3995             var val = null;
3996             var el = this.getEl();
3997
3998             if (attr == 'scroll') {
3999                 val = [ el.scrollLeft, el.scrollTop ];
4000             } else {
4001                 val = superclass.getAttribute.call(this, attr);
4002             }
4003
4004             return val;
4005         };
4006
4007         proto.setAttribute = function(attr, val, unit) {
4008             var el = this.getEl();
4009
4010             if (attr == 'scroll') {
4011                 el.scrollLeft = val[0];
4012                 el.scrollTop = val[1];
4013             } else {
4014                 superclass.setAttribute.call(this, attr, val, unit);
4015             }
4016         };
4017     })();
4018 /*
4019  * Based on:
4020  * Ext JS Library 1.1.1
4021  * Copyright(c) 2006-2007, Ext JS, LLC.
4022  *
4023  * Originally Released Under LGPL - original licence link has changed is not relivant.
4024  *
4025  * Fork - LGPL
4026  * <script type="text/javascript">
4027  */
4028  
4029
4030 /**
4031  * @class Roo.DomHelper
4032  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4033  * 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>.
4034  * @singleton
4035  */
4036 Roo.DomHelper = function(){
4037     var tempTableEl = null;
4038     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4039     var tableRe = /^table|tbody|tr|td$/i;
4040     var xmlns = {};
4041     // build as innerHTML where available
4042     /** @ignore */
4043     var createHtml = function(o){
4044         if(typeof o == 'string'){
4045             return o;
4046         }
4047         var b = "";
4048         if(!o.tag){
4049             o.tag = "div";
4050         }
4051         b += "<" + o.tag;
4052         for(var attr in o){
4053             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4054             if(attr == "style"){
4055                 var s = o["style"];
4056                 if(typeof s == "function"){
4057                     s = s.call();
4058                 }
4059                 if(typeof s == "string"){
4060                     b += ' style="' + s + '"';
4061                 }else if(typeof s == "object"){
4062                     b += ' style="';
4063                     for(var key in s){
4064                         if(typeof s[key] != "function"){
4065                             b += key + ":" + s[key] + ";";
4066                         }
4067                     }
4068                     b += '"';
4069                 }
4070             }else{
4071                 if(attr == "cls"){
4072                     b += ' class="' + o["cls"] + '"';
4073                 }else if(attr == "htmlFor"){
4074                     b += ' for="' + o["htmlFor"] + '"';
4075                 }else{
4076                     b += " " + attr + '="' + o[attr] + '"';
4077                 }
4078             }
4079         }
4080         if(emptyTags.test(o.tag)){
4081             b += "/>";
4082         }else{
4083             b += ">";
4084             var cn = o.children || o.cn;
4085             if(cn){
4086                 //http://bugs.kde.org/show_bug.cgi?id=71506
4087                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4088                     for(var i = 0, len = cn.length; i < len; i++) {
4089                         b += createHtml(cn[i], b);
4090                     }
4091                 }else{
4092                     b += createHtml(cn, b);
4093                 }
4094             }
4095             if(o.html){
4096                 b += o.html;
4097             }
4098             b += "</" + o.tag + ">";
4099         }
4100         return b;
4101     };
4102
4103     // build as dom
4104     /** @ignore */
4105     var createDom = function(o, parentNode){
4106          
4107         // defininition craeted..
4108         var ns = false;
4109         if (o.ns && o.ns != 'html') {
4110                
4111             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4112                 xmlns[o.ns] = o.xmlns;
4113                 ns = o.xmlns;
4114             }
4115             if (typeof(xmlns[o.ns]) == 'undefined') {
4116                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4117             }
4118             ns = xmlns[o.ns];
4119         }
4120         
4121         
4122         if (typeof(o) == 'string') {
4123             return parentNode.appendChild(document.createTextNode(o));
4124         }
4125         o.tag = o.tag || div;
4126         if (o.ns && Roo.isIE) {
4127             ns = false;
4128             o.tag = o.ns + ':' + o.tag;
4129             
4130         }
4131         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4132         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4133         for(var attr in o){
4134             
4135             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4136                     attr == "style" || typeof o[attr] == "function") continue;
4137                     
4138             if(attr=="cls" && Roo.isIE){
4139                 el.className = o["cls"];
4140             }else{
4141                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4142                 else el[attr] = o[attr];
4143             }
4144         }
4145         Roo.DomHelper.applyStyles(el, o.style);
4146         var cn = o.children || o.cn;
4147         if(cn){
4148             //http://bugs.kde.org/show_bug.cgi?id=71506
4149              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4150                 for(var i = 0, len = cn.length; i < len; i++) {
4151                     createDom(cn[i], el);
4152                 }
4153             }else{
4154                 createDom(cn, el);
4155             }
4156         }
4157         if(o.html){
4158             el.innerHTML = o.html;
4159         }
4160         if(parentNode){
4161            parentNode.appendChild(el);
4162         }
4163         return el;
4164     };
4165
4166     var ieTable = function(depth, s, h, e){
4167         tempTableEl.innerHTML = [s, h, e].join('');
4168         var i = -1, el = tempTableEl;
4169         while(++i < depth){
4170             el = el.firstChild;
4171         }
4172         return el;
4173     };
4174
4175     // kill repeat to save bytes
4176     var ts = '<table>',
4177         te = '</table>',
4178         tbs = ts+'<tbody>',
4179         tbe = '</tbody>'+te,
4180         trs = tbs + '<tr>',
4181         tre = '</tr>'+tbe;
4182
4183     /**
4184      * @ignore
4185      * Nasty code for IE's broken table implementation
4186      */
4187     var insertIntoTable = function(tag, where, el, html){
4188         if(!tempTableEl){
4189             tempTableEl = document.createElement('div');
4190         }
4191         var node;
4192         var before = null;
4193         if(tag == 'td'){
4194             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4195                 return;
4196             }
4197             if(where == 'beforebegin'){
4198                 before = el;
4199                 el = el.parentNode;
4200             } else{
4201                 before = el.nextSibling;
4202                 el = el.parentNode;
4203             }
4204             node = ieTable(4, trs, html, tre);
4205         }
4206         else if(tag == 'tr'){
4207             if(where == 'beforebegin'){
4208                 before = el;
4209                 el = el.parentNode;
4210                 node = ieTable(3, tbs, html, tbe);
4211             } else if(where == 'afterend'){
4212                 before = el.nextSibling;
4213                 el = el.parentNode;
4214                 node = ieTable(3, tbs, html, tbe);
4215             } else{ // INTO a TR
4216                 if(where == 'afterbegin'){
4217                     before = el.firstChild;
4218                 }
4219                 node = ieTable(4, trs, html, tre);
4220             }
4221         } else if(tag == 'tbody'){
4222             if(where == 'beforebegin'){
4223                 before = el;
4224                 el = el.parentNode;
4225                 node = ieTable(2, ts, html, te);
4226             } else if(where == 'afterend'){
4227                 before = el.nextSibling;
4228                 el = el.parentNode;
4229                 node = ieTable(2, ts, html, te);
4230             } else{
4231                 if(where == 'afterbegin'){
4232                     before = el.firstChild;
4233                 }
4234                 node = ieTable(3, tbs, html, tbe);
4235             }
4236         } else{ // TABLE
4237             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4238                 return;
4239             }
4240             if(where == 'afterbegin'){
4241                 before = el.firstChild;
4242             }
4243             node = ieTable(2, ts, html, te);
4244         }
4245         el.insertBefore(node, before);
4246         return node;
4247     };
4248
4249     return {
4250     /** True to force the use of DOM instead of html fragments @type Boolean */
4251     useDom : false,
4252
4253     /**
4254      * Returns the markup for the passed Element(s) config
4255      * @param {Object} o The Dom object spec (and children)
4256      * @return {String}
4257      */
4258     markup : function(o){
4259         return createHtml(o);
4260     },
4261
4262     /**
4263      * Applies a style specification to an element
4264      * @param {String/HTMLElement} el The element to apply styles to
4265      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4266      * a function which returns such a specification.
4267      */
4268     applyStyles : function(el, styles){
4269         if(styles){
4270            el = Roo.fly(el);
4271            if(typeof styles == "string"){
4272                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4273                var matches;
4274                while ((matches = re.exec(styles)) != null){
4275                    el.setStyle(matches[1], matches[2]);
4276                }
4277            }else if (typeof styles == "object"){
4278                for (var style in styles){
4279                   el.setStyle(style, styles[style]);
4280                }
4281            }else if (typeof styles == "function"){
4282                 Roo.DomHelper.applyStyles(el, styles.call());
4283            }
4284         }
4285     },
4286
4287     /**
4288      * Inserts an HTML fragment into the Dom
4289      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4290      * @param {HTMLElement} el The context element
4291      * @param {String} html The HTML fragmenet
4292      * @return {HTMLElement} The new node
4293      */
4294     insertHtml : function(where, el, html){
4295         where = where.toLowerCase();
4296         if(el.insertAdjacentHTML){
4297             if(tableRe.test(el.tagName)){
4298                 var rs;
4299                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4300                     return rs;
4301                 }
4302             }
4303             switch(where){
4304                 case "beforebegin":
4305                     el.insertAdjacentHTML('BeforeBegin', html);
4306                     return el.previousSibling;
4307                 case "afterbegin":
4308                     el.insertAdjacentHTML('AfterBegin', html);
4309                     return el.firstChild;
4310                 case "beforeend":
4311                     el.insertAdjacentHTML('BeforeEnd', html);
4312                     return el.lastChild;
4313                 case "afterend":
4314                     el.insertAdjacentHTML('AfterEnd', html);
4315                     return el.nextSibling;
4316             }
4317             throw 'Illegal insertion point -> "' + where + '"';
4318         }
4319         var range = el.ownerDocument.createRange();
4320         var frag;
4321         switch(where){
4322              case "beforebegin":
4323                 range.setStartBefore(el);
4324                 frag = range.createContextualFragment(html);
4325                 el.parentNode.insertBefore(frag, el);
4326                 return el.previousSibling;
4327              case "afterbegin":
4328                 if(el.firstChild){
4329                     range.setStartBefore(el.firstChild);
4330                     frag = range.createContextualFragment(html);
4331                     el.insertBefore(frag, el.firstChild);
4332                     return el.firstChild;
4333                 }else{
4334                     el.innerHTML = html;
4335                     return el.firstChild;
4336                 }
4337             case "beforeend":
4338                 if(el.lastChild){
4339                     range.setStartAfter(el.lastChild);
4340                     frag = range.createContextualFragment(html);
4341                     el.appendChild(frag);
4342                     return el.lastChild;
4343                 }else{
4344                     el.innerHTML = html;
4345                     return el.lastChild;
4346                 }
4347             case "afterend":
4348                 range.setStartAfter(el);
4349                 frag = range.createContextualFragment(html);
4350                 el.parentNode.insertBefore(frag, el.nextSibling);
4351                 return el.nextSibling;
4352             }
4353             throw 'Illegal insertion point -> "' + where + '"';
4354     },
4355
4356     /**
4357      * Creates new Dom element(s) and inserts them before el
4358      * @param {String/HTMLElement/Element} el The context element
4359      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4360      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4361      * @return {HTMLElement/Roo.Element} The new node
4362      */
4363     insertBefore : function(el, o, returnElement){
4364         return this.doInsert(el, o, returnElement, "beforeBegin");
4365     },
4366
4367     /**
4368      * Creates new Dom element(s) and inserts them after el
4369      * @param {String/HTMLElement/Element} el The context element
4370      * @param {Object} o The Dom object spec (and children)
4371      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4372      * @return {HTMLElement/Roo.Element} The new node
4373      */
4374     insertAfter : function(el, o, returnElement){
4375         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4376     },
4377
4378     /**
4379      * Creates new Dom element(s) and inserts them as the first child of el
4380      * @param {String/HTMLElement/Element} el The context element
4381      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4382      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4383      * @return {HTMLElement/Roo.Element} The new node
4384      */
4385     insertFirst : function(el, o, returnElement){
4386         return this.doInsert(el, o, returnElement, "afterBegin");
4387     },
4388
4389     // private
4390     doInsert : function(el, o, returnElement, pos, sibling){
4391         el = Roo.getDom(el);
4392         var newNode;
4393         if(this.useDom || o.ns){
4394             newNode = createDom(o, null);
4395             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4396         }else{
4397             var html = createHtml(o);
4398             newNode = this.insertHtml(pos, el, html);
4399         }
4400         return returnElement ? Roo.get(newNode, true) : newNode;
4401     },
4402
4403     /**
4404      * Creates new Dom element(s) and appends them to el
4405      * @param {String/HTMLElement/Element} el The context element
4406      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4407      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4408      * @return {HTMLElement/Roo.Element} The new node
4409      */
4410     append : function(el, o, returnElement){
4411         el = Roo.getDom(el);
4412         var newNode;
4413         if(this.useDom || o.ns){
4414             newNode = createDom(o, null);
4415             el.appendChild(newNode);
4416         }else{
4417             var html = createHtml(o);
4418             newNode = this.insertHtml("beforeEnd", el, html);
4419         }
4420         return returnElement ? Roo.get(newNode, true) : newNode;
4421     },
4422
4423     /**
4424      * Creates new Dom element(s) and overwrites the contents of el with them
4425      * @param {String/HTMLElement/Element} el The context element
4426      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4427      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4428      * @return {HTMLElement/Roo.Element} The new node
4429      */
4430     overwrite : function(el, o, returnElement){
4431         el = Roo.getDom(el);
4432         if (o.ns) {
4433           
4434             while (el.childNodes.length) {
4435                 el.removeChild(el.firstChild);
4436             }
4437             createDom(o, el);
4438         } else {
4439             el.innerHTML = createHtml(o);   
4440         }
4441         
4442         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4443     },
4444
4445     /**
4446      * Creates a new Roo.DomHelper.Template from the Dom object spec
4447      * @param {Object} o The Dom object spec (and children)
4448      * @return {Roo.DomHelper.Template} The new template
4449      */
4450     createTemplate : function(o){
4451         var html = createHtml(o);
4452         return new Roo.Template(html);
4453     }
4454     };
4455 }();
4456 /*
4457  * Based on:
4458  * Ext JS Library 1.1.1
4459  * Copyright(c) 2006-2007, Ext JS, LLC.
4460  *
4461  * Originally Released Under LGPL - original licence link has changed is not relivant.
4462  *
4463  * Fork - LGPL
4464  * <script type="text/javascript">
4465  */
4466  
4467 /**
4468 * @class Roo.Template
4469 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4470 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4471 * Usage:
4472 <pre><code>
4473 var t = new Roo.Template({
4474     html :  '&lt;div name="{id}"&gt;' + 
4475         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4476         '&lt;/div&gt;',
4477     myformat: function (value, allValues) {
4478         return 'XX' + value;
4479     }
4480 });
4481 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4482 </code></pre>
4483 * 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>. 
4484 * @constructor
4485 * @param {Object} cfg - Configuration object.
4486 */
4487 Roo.Template = function(cfg){
4488     // BC!
4489     if(cfg instanceof Array){
4490         cfg = cfg.join("");
4491     }else if(arguments.length > 1){
4492         cfg = Array.prototype.join.call(arguments, "");
4493     }
4494     
4495     
4496     if (typeof(cfg) == 'object') {
4497         Roo.apply(this,cfg)
4498     } else {
4499         // bc
4500         this.html = cfg;
4501     }
4502     
4503     
4504 };
4505 Roo.Template.prototype = {
4506     
4507     /**
4508      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4509      */
4510     html : '',
4511     /**
4512      * Returns an HTML fragment of this template with the specified values applied.
4513      * @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'})
4514      * @return {String} The HTML fragment
4515      */
4516     applyTemplate : function(values){
4517         try {
4518             
4519             if(this.compiled){
4520                 return this.compiled(values);
4521             }
4522             var useF = this.disableFormats !== true;
4523             var fm = Roo.util.Format, tpl = this;
4524             var fn = function(m, name, format, args){
4525                 if(format && useF){
4526                     if(format.substr(0, 5) == "this."){
4527                         return tpl.call(format.substr(5), values[name], values);
4528                     }else{
4529                         if(args){
4530                             // quoted values are required for strings in compiled templates, 
4531                             // but for non compiled we need to strip them
4532                             // quoted reversed for jsmin
4533                             var re = /^\s*['"](.*)["']\s*$/;
4534                             args = args.split(',');
4535                             for(var i = 0, len = args.length; i < len; i++){
4536                                 args[i] = args[i].replace(re, "$1");
4537                             }
4538                             args = [values[name]].concat(args);
4539                         }else{
4540                             args = [values[name]];
4541                         }
4542                         return fm[format].apply(fm, args);
4543                     }
4544                 }else{
4545                     return values[name] !== undefined ? values[name] : "";
4546                 }
4547             };
4548             return this.html.replace(this.re, fn);
4549         } catch (e) {
4550             Roo.log(e);
4551             throw e;
4552         }
4553          
4554     },
4555     
4556     /**
4557      * Sets the HTML used as the template and optionally compiles it.
4558      * @param {String} html
4559      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4560      * @return {Roo.Template} this
4561      */
4562     set : function(html, compile){
4563         this.html = html;
4564         this.compiled = null;
4565         if(compile){
4566             this.compile();
4567         }
4568         return this;
4569     },
4570     
4571     /**
4572      * True to disable format functions (defaults to false)
4573      * @type Boolean
4574      */
4575     disableFormats : false,
4576     
4577     /**
4578     * The regular expression used to match template variables 
4579     * @type RegExp
4580     * @property 
4581     */
4582     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4583     
4584     /**
4585      * Compiles the template into an internal function, eliminating the RegEx overhead.
4586      * @return {Roo.Template} this
4587      */
4588     compile : function(){
4589         var fm = Roo.util.Format;
4590         var useF = this.disableFormats !== true;
4591         var sep = Roo.isGecko ? "+" : ",";
4592         var fn = function(m, name, format, args){
4593             if(format && useF){
4594                 args = args ? ',' + args : "";
4595                 if(format.substr(0, 5) != "this."){
4596                     format = "fm." + format + '(';
4597                 }else{
4598                     format = 'this.call("'+ format.substr(5) + '", ';
4599                     args = ", values";
4600                 }
4601             }else{
4602                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4603             }
4604             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4605         };
4606         var body;
4607         // branched to use + in gecko and [].join() in others
4608         if(Roo.isGecko){
4609             body = "this.compiled = function(values){ return '" +
4610                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4611                     "';};";
4612         }else{
4613             body = ["this.compiled = function(values){ return ['"];
4614             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4615             body.push("'].join('');};");
4616             body = body.join('');
4617         }
4618         /**
4619          * eval:var:values
4620          * eval:var:fm
4621          */
4622         eval(body);
4623         return this;
4624     },
4625     
4626     // private function used to call members
4627     call : function(fnName, value, allValues){
4628         return this[fnName](value, allValues);
4629     },
4630     
4631     /**
4632      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4633      * @param {String/HTMLElement/Roo.Element} el The context element
4634      * @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'})
4635      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4636      * @return {HTMLElement/Roo.Element} The new node or Element
4637      */
4638     insertFirst: function(el, values, returnElement){
4639         return this.doInsert('afterBegin', el, values, returnElement);
4640     },
4641
4642     /**
4643      * Applies the supplied values to the template and inserts the new node(s) before el.
4644      * @param {String/HTMLElement/Roo.Element} el The context element
4645      * @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'})
4646      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4647      * @return {HTMLElement/Roo.Element} The new node or Element
4648      */
4649     insertBefore: function(el, values, returnElement){
4650         return this.doInsert('beforeBegin', el, values, returnElement);
4651     },
4652
4653     /**
4654      * Applies the supplied values to the template and inserts the new node(s) after el.
4655      * @param {String/HTMLElement/Roo.Element} el The context element
4656      * @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'})
4657      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4658      * @return {HTMLElement/Roo.Element} The new node or Element
4659      */
4660     insertAfter : function(el, values, returnElement){
4661         return this.doInsert('afterEnd', el, values, returnElement);
4662     },
4663     
4664     /**
4665      * Applies the supplied values to the template and appends the new node(s) to el.
4666      * @param {String/HTMLElement/Roo.Element} el The context element
4667      * @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'})
4668      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4669      * @return {HTMLElement/Roo.Element} The new node or Element
4670      */
4671     append : function(el, values, returnElement){
4672         return this.doInsert('beforeEnd', el, values, returnElement);
4673     },
4674
4675     doInsert : function(where, el, values, returnEl){
4676         el = Roo.getDom(el);
4677         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4678         return returnEl ? Roo.get(newNode, true) : newNode;
4679     },
4680
4681     /**
4682      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
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     overwrite : function(el, values, returnElement){
4689         el = Roo.getDom(el);
4690         el.innerHTML = this.applyTemplate(values);
4691         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4692     }
4693 };
4694 /**
4695  * Alias for {@link #applyTemplate}
4696  * @method
4697  */
4698 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4699
4700 // backwards compat
4701 Roo.DomHelper.Template = Roo.Template;
4702
4703 /**
4704  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4705  * @param {String/HTMLElement} el A DOM element or its id
4706  * @returns {Roo.Template} The created template
4707  * @static
4708  */
4709 Roo.Template.from = function(el){
4710     el = Roo.getDom(el);
4711     return new Roo.Template(el.value || el.innerHTML);
4712 };/*
4713  * Based on:
4714  * Ext JS Library 1.1.1
4715  * Copyright(c) 2006-2007, Ext JS, LLC.
4716  *
4717  * Originally Released Under LGPL - original licence link has changed is not relivant.
4718  *
4719  * Fork - LGPL
4720  * <script type="text/javascript">
4721  */
4722  
4723
4724 /*
4725  * This is code is also distributed under MIT license for use
4726  * with jQuery and prototype JavaScript libraries.
4727  */
4728 /**
4729  * @class Roo.DomQuery
4730 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).
4731 <p>
4732 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>
4733
4734 <p>
4735 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.
4736 </p>
4737 <h4>Element Selectors:</h4>
4738 <ul class="list">
4739     <li> <b>*</b> any element</li>
4740     <li> <b>E</b> an element with the tag E</li>
4741     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4742     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4743     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4744     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4745 </ul>
4746 <h4>Attribute Selectors:</h4>
4747 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4748 <ul class="list">
4749     <li> <b>E[foo]</b> has an attribute "foo"</li>
4750     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4751     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4752     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4753     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4754     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4755     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4756 </ul>
4757 <h4>Pseudo Classes:</h4>
4758 <ul class="list">
4759     <li> <b>E:first-child</b> E is the first child of its parent</li>
4760     <li> <b>E:last-child</b> E is the last child of its parent</li>
4761     <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>
4762     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4763     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4764     <li> <b>E:only-child</b> E is the only child of its parent</li>
4765     <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>
4766     <li> <b>E:first</b> the first E in the resultset</li>
4767     <li> <b>E:last</b> the last E in the resultset</li>
4768     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4769     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4770     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4771     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4772     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4773     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4774     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4775     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4776     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4777 </ul>
4778 <h4>CSS Value Selectors:</h4>
4779 <ul class="list">
4780     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4781     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4782     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4783     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4784     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4785     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4786 </ul>
4787  * @singleton
4788  */
4789 Roo.DomQuery = function(){
4790     var cache = {}, simpleCache = {}, valueCache = {};
4791     var nonSpace = /\S/;
4792     var trimRe = /^\s+|\s+$/g;
4793     var tplRe = /\{(\d+)\}/g;
4794     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4795     var tagTokenRe = /^(#)?([\w-\*]+)/;
4796     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4797
4798     function child(p, index){
4799         var i = 0;
4800         var n = p.firstChild;
4801         while(n){
4802             if(n.nodeType == 1){
4803                if(++i == index){
4804                    return n;
4805                }
4806             }
4807             n = n.nextSibling;
4808         }
4809         return null;
4810     };
4811
4812     function next(n){
4813         while((n = n.nextSibling) && n.nodeType != 1);
4814         return n;
4815     };
4816
4817     function prev(n){
4818         while((n = n.previousSibling) && n.nodeType != 1);
4819         return n;
4820     };
4821
4822     function children(d){
4823         var n = d.firstChild, ni = -1;
4824             while(n){
4825                 var nx = n.nextSibling;
4826                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4827                     d.removeChild(n);
4828                 }else{
4829                     n.nodeIndex = ++ni;
4830                 }
4831                 n = nx;
4832             }
4833             return this;
4834         };
4835
4836     function byClassName(c, a, v){
4837         if(!v){
4838             return c;
4839         }
4840         var r = [], ri = -1, cn;
4841         for(var i = 0, ci; ci = c[i]; i++){
4842             if((' '+ci.className+' ').indexOf(v) != -1){
4843                 r[++ri] = ci;
4844             }
4845         }
4846         return r;
4847     };
4848
4849     function attrValue(n, attr){
4850         if(!n.tagName && typeof n.length != "undefined"){
4851             n = n[0];
4852         }
4853         if(!n){
4854             return null;
4855         }
4856         if(attr == "for"){
4857             return n.htmlFor;
4858         }
4859         if(attr == "class" || attr == "className"){
4860             return n.className;
4861         }
4862         return n.getAttribute(attr) || n[attr];
4863
4864     };
4865
4866     function getNodes(ns, mode, tagName){
4867         var result = [], ri = -1, cs;
4868         if(!ns){
4869             return result;
4870         }
4871         tagName = tagName || "*";
4872         if(typeof ns.getElementsByTagName != "undefined"){
4873             ns = [ns];
4874         }
4875         if(!mode){
4876             for(var i = 0, ni; ni = ns[i]; i++){
4877                 cs = ni.getElementsByTagName(tagName);
4878                 for(var j = 0, ci; ci = cs[j]; j++){
4879                     result[++ri] = ci;
4880                 }
4881             }
4882         }else if(mode == "/" || mode == ">"){
4883             var utag = tagName.toUpperCase();
4884             for(var i = 0, ni, cn; ni = ns[i]; i++){
4885                 cn = ni.children || ni.childNodes;
4886                 for(var j = 0, cj; cj = cn[j]; j++){
4887                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4888                         result[++ri] = cj;
4889                     }
4890                 }
4891             }
4892         }else if(mode == "+"){
4893             var utag = tagName.toUpperCase();
4894             for(var i = 0, n; n = ns[i]; i++){
4895                 while((n = n.nextSibling) && n.nodeType != 1);
4896                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4897                     result[++ri] = n;
4898                 }
4899             }
4900         }else if(mode == "~"){
4901             for(var i = 0, n; n = ns[i]; i++){
4902                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4903                 if(n){
4904                     result[++ri] = n;
4905                 }
4906             }
4907         }
4908         return result;
4909     };
4910
4911     function concat(a, b){
4912         if(b.slice){
4913             return a.concat(b);
4914         }
4915         for(var i = 0, l = b.length; i < l; i++){
4916             a[a.length] = b[i];
4917         }
4918         return a;
4919     }
4920
4921     function byTag(cs, tagName){
4922         if(cs.tagName || cs == document){
4923             cs = [cs];
4924         }
4925         if(!tagName){
4926             return cs;
4927         }
4928         var r = [], ri = -1;
4929         tagName = tagName.toLowerCase();
4930         for(var i = 0, ci; ci = cs[i]; i++){
4931             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4932                 r[++ri] = ci;
4933             }
4934         }
4935         return r;
4936     };
4937
4938     function byId(cs, attr, id){
4939         if(cs.tagName || cs == document){
4940             cs = [cs];
4941         }
4942         if(!id){
4943             return cs;
4944         }
4945         var r = [], ri = -1;
4946         for(var i = 0,ci; ci = cs[i]; i++){
4947             if(ci && ci.id == id){
4948                 r[++ri] = ci;
4949                 return r;
4950             }
4951         }
4952         return r;
4953     };
4954
4955     function byAttribute(cs, attr, value, op, custom){
4956         var r = [], ri = -1, st = custom=="{";
4957         var f = Roo.DomQuery.operators[op];
4958         for(var i = 0, ci; ci = cs[i]; i++){
4959             var a;
4960             if(st){
4961                 a = Roo.DomQuery.getStyle(ci, attr);
4962             }
4963             else if(attr == "class" || attr == "className"){
4964                 a = ci.className;
4965             }else if(attr == "for"){
4966                 a = ci.htmlFor;
4967             }else if(attr == "href"){
4968                 a = ci.getAttribute("href", 2);
4969             }else{
4970                 a = ci.getAttribute(attr);
4971             }
4972             if((f && f(a, value)) || (!f && a)){
4973                 r[++ri] = ci;
4974             }
4975         }
4976         return r;
4977     };
4978
4979     function byPseudo(cs, name, value){
4980         return Roo.DomQuery.pseudos[name](cs, value);
4981     };
4982
4983     // This is for IE MSXML which does not support expandos.
4984     // IE runs the same speed using setAttribute, however FF slows way down
4985     // and Safari completely fails so they need to continue to use expandos.
4986     var isIE = window.ActiveXObject ? true : false;
4987
4988     // this eval is stop the compressor from
4989     // renaming the variable to something shorter
4990     
4991     /** eval:var:batch */
4992     var batch = 30803; 
4993
4994     var key = 30803;
4995
4996     function nodupIEXml(cs){
4997         var d = ++key;
4998         cs[0].setAttribute("_nodup", d);
4999         var r = [cs[0]];
5000         for(var i = 1, len = cs.length; i < len; i++){
5001             var c = cs[i];
5002             if(!c.getAttribute("_nodup") != d){
5003                 c.setAttribute("_nodup", d);
5004                 r[r.length] = c;
5005             }
5006         }
5007         for(var i = 0, len = cs.length; i < len; i++){
5008             cs[i].removeAttribute("_nodup");
5009         }
5010         return r;
5011     }
5012
5013     function nodup(cs){
5014         if(!cs){
5015             return [];
5016         }
5017         var len = cs.length, c, i, r = cs, cj, ri = -1;
5018         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5019             return cs;
5020         }
5021         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5022             return nodupIEXml(cs);
5023         }
5024         var d = ++key;
5025         cs[0]._nodup = d;
5026         for(i = 1; c = cs[i]; i++){
5027             if(c._nodup != d){
5028                 c._nodup = d;
5029             }else{
5030                 r = [];
5031                 for(var j = 0; j < i; j++){
5032                     r[++ri] = cs[j];
5033                 }
5034                 for(j = i+1; cj = cs[j]; j++){
5035                     if(cj._nodup != d){
5036                         cj._nodup = d;
5037                         r[++ri] = cj;
5038                     }
5039                 }
5040                 return r;
5041             }
5042         }
5043         return r;
5044     }
5045
5046     function quickDiffIEXml(c1, c2){
5047         var d = ++key;
5048         for(var i = 0, len = c1.length; i < len; i++){
5049             c1[i].setAttribute("_qdiff", d);
5050         }
5051         var r = [];
5052         for(var i = 0, len = c2.length; i < len; i++){
5053             if(c2[i].getAttribute("_qdiff") != d){
5054                 r[r.length] = c2[i];
5055             }
5056         }
5057         for(var i = 0, len = c1.length; i < len; i++){
5058            c1[i].removeAttribute("_qdiff");
5059         }
5060         return r;
5061     }
5062
5063     function quickDiff(c1, c2){
5064         var len1 = c1.length;
5065         if(!len1){
5066             return c2;
5067         }
5068         if(isIE && c1[0].selectSingleNode){
5069             return quickDiffIEXml(c1, c2);
5070         }
5071         var d = ++key;
5072         for(var i = 0; i < len1; i++){
5073             c1[i]._qdiff = d;
5074         }
5075         var r = [];
5076         for(var i = 0, len = c2.length; i < len; i++){
5077             if(c2[i]._qdiff != d){
5078                 r[r.length] = c2[i];
5079             }
5080         }
5081         return r;
5082     }
5083
5084     function quickId(ns, mode, root, id){
5085         if(ns == root){
5086            var d = root.ownerDocument || root;
5087            return d.getElementById(id);
5088         }
5089         ns = getNodes(ns, mode, "*");
5090         return byId(ns, null, id);
5091     }
5092
5093     return {
5094         getStyle : function(el, name){
5095             return Roo.fly(el).getStyle(name);
5096         },
5097         /**
5098          * Compiles a selector/xpath query into a reusable function. The returned function
5099          * takes one parameter "root" (optional), which is the context node from where the query should start.
5100          * @param {String} selector The selector/xpath query
5101          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5102          * @return {Function}
5103          */
5104         compile : function(path, type){
5105             type = type || "select";
5106             
5107             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5108             var q = path, mode, lq;
5109             var tk = Roo.DomQuery.matchers;
5110             var tklen = tk.length;
5111             var mm;
5112
5113             // accept leading mode switch
5114             var lmode = q.match(modeRe);
5115             if(lmode && lmode[1]){
5116                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5117                 q = q.replace(lmode[1], "");
5118             }
5119             // strip leading slashes
5120             while(path.substr(0, 1)=="/"){
5121                 path = path.substr(1);
5122             }
5123
5124             while(q && lq != q){
5125                 lq = q;
5126                 var tm = q.match(tagTokenRe);
5127                 if(type == "select"){
5128                     if(tm){
5129                         if(tm[1] == "#"){
5130                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5131                         }else{
5132                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5133                         }
5134                         q = q.replace(tm[0], "");
5135                     }else if(q.substr(0, 1) != '@'){
5136                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5137                     }
5138                 }else{
5139                     if(tm){
5140                         if(tm[1] == "#"){
5141                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5142                         }else{
5143                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5144                         }
5145                         q = q.replace(tm[0], "");
5146                     }
5147                 }
5148                 while(!(mm = q.match(modeRe))){
5149                     var matched = false;
5150                     for(var j = 0; j < tklen; j++){
5151                         var t = tk[j];
5152                         var m = q.match(t.re);
5153                         if(m){
5154                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5155                                                     return m[i];
5156                                                 });
5157                             q = q.replace(m[0], "");
5158                             matched = true;
5159                             break;
5160                         }
5161                     }
5162                     // prevent infinite loop on bad selector
5163                     if(!matched){
5164                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5165                     }
5166                 }
5167                 if(mm[1]){
5168                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5169                     q = q.replace(mm[1], "");
5170                 }
5171             }
5172             fn[fn.length] = "return nodup(n);\n}";
5173             
5174              /** 
5175               * list of variables that need from compression as they are used by eval.
5176              *  eval:var:batch 
5177              *  eval:var:nodup
5178              *  eval:var:byTag
5179              *  eval:var:ById
5180              *  eval:var:getNodes
5181              *  eval:var:quickId
5182              *  eval:var:mode
5183              *  eval:var:root
5184              *  eval:var:n
5185              *  eval:var:byClassName
5186              *  eval:var:byPseudo
5187              *  eval:var:byAttribute
5188              *  eval:var:attrValue
5189              * 
5190              **/ 
5191             eval(fn.join(""));
5192             return f;
5193         },
5194
5195         /**
5196          * Selects a group of elements.
5197          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5198          * @param {Node} root (optional) The start of the query (defaults to document).
5199          * @return {Array}
5200          */
5201         select : function(path, root, type){
5202             if(!root || root == document){
5203                 root = document;
5204             }
5205             if(typeof root == "string"){
5206                 root = document.getElementById(root);
5207             }
5208             var paths = path.split(",");
5209             var results = [];
5210             for(var i = 0, len = paths.length; i < len; i++){
5211                 var p = paths[i].replace(trimRe, "");
5212                 if(!cache[p]){
5213                     cache[p] = Roo.DomQuery.compile(p);
5214                     if(!cache[p]){
5215                         throw p + " is not a valid selector";
5216                     }
5217                 }
5218                 var result = cache[p](root);
5219                 if(result && result != document){
5220                     results = results.concat(result);
5221                 }
5222             }
5223             if(paths.length > 1){
5224                 return nodup(results);
5225             }
5226             return results;
5227         },
5228
5229         /**
5230          * Selects a single element.
5231          * @param {String} selector The selector/xpath query
5232          * @param {Node} root (optional) The start of the query (defaults to document).
5233          * @return {Element}
5234          */
5235         selectNode : function(path, root){
5236             return Roo.DomQuery.select(path, root)[0];
5237         },
5238
5239         /**
5240          * Selects the value of a node, optionally replacing null with the defaultValue.
5241          * @param {String} selector The selector/xpath query
5242          * @param {Node} root (optional) The start of the query (defaults to document).
5243          * @param {String} defaultValue
5244          */
5245         selectValue : function(path, root, defaultValue){
5246             path = path.replace(trimRe, "");
5247             if(!valueCache[path]){
5248                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5249             }
5250             var n = valueCache[path](root);
5251             n = n[0] ? n[0] : n;
5252             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5253             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5254         },
5255
5256         /**
5257          * Selects the value of a node, parsing integers and floats.
5258          * @param {String} selector The selector/xpath query
5259          * @param {Node} root (optional) The start of the query (defaults to document).
5260          * @param {Number} defaultValue
5261          * @return {Number}
5262          */
5263         selectNumber : function(path, root, defaultValue){
5264             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5265             return parseFloat(v);
5266         },
5267
5268         /**
5269          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5270          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5271          * @param {String} selector The simple selector to test
5272          * @return {Boolean}
5273          */
5274         is : function(el, ss){
5275             if(typeof el == "string"){
5276                 el = document.getElementById(el);
5277             }
5278             var isArray = (el instanceof Array);
5279             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5280             return isArray ? (result.length == el.length) : (result.length > 0);
5281         },
5282
5283         /**
5284          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5285          * @param {Array} el An array of elements to filter
5286          * @param {String} selector The simple selector to test
5287          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5288          * the selector instead of the ones that match
5289          * @return {Array}
5290          */
5291         filter : function(els, ss, nonMatches){
5292             ss = ss.replace(trimRe, "");
5293             if(!simpleCache[ss]){
5294                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5295             }
5296             var result = simpleCache[ss](els);
5297             return nonMatches ? quickDiff(result, els) : result;
5298         },
5299
5300         /**
5301          * Collection of matching regular expressions and code snippets.
5302          */
5303         matchers : [{
5304                 re: /^\.([\w-]+)/,
5305                 select: 'n = byClassName(n, null, " {1} ");'
5306             }, {
5307                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5308                 select: 'n = byPseudo(n, "{1}", "{2}");'
5309             },{
5310                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5311                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5312             }, {
5313                 re: /^#([\w-]+)/,
5314                 select: 'n = byId(n, null, "{1}");'
5315             },{
5316                 re: /^@([\w-]+)/,
5317                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5318             }
5319         ],
5320
5321         /**
5322          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5323          * 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;.
5324          */
5325         operators : {
5326             "=" : function(a, v){
5327                 return a == v;
5328             },
5329             "!=" : function(a, v){
5330                 return a != v;
5331             },
5332             "^=" : function(a, v){
5333                 return a && a.substr(0, v.length) == v;
5334             },
5335             "$=" : function(a, v){
5336                 return a && a.substr(a.length-v.length) == v;
5337             },
5338             "*=" : function(a, v){
5339                 return a && a.indexOf(v) !== -1;
5340             },
5341             "%=" : function(a, v){
5342                 return (a % v) == 0;
5343             },
5344             "|=" : function(a, v){
5345                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5346             },
5347             "~=" : function(a, v){
5348                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5349             }
5350         },
5351
5352         /**
5353          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5354          * and the argument (if any) supplied in the selector.
5355          */
5356         pseudos : {
5357             "first-child" : function(c){
5358                 var r = [], ri = -1, n;
5359                 for(var i = 0, ci; ci = n = c[i]; i++){
5360                     while((n = n.previousSibling) && n.nodeType != 1);
5361                     if(!n){
5362                         r[++ri] = ci;
5363                     }
5364                 }
5365                 return r;
5366             },
5367
5368             "last-child" : function(c){
5369                 var r = [], ri = -1, n;
5370                 for(var i = 0, ci; ci = n = c[i]; i++){
5371                     while((n = n.nextSibling) && n.nodeType != 1);
5372                     if(!n){
5373                         r[++ri] = ci;
5374                     }
5375                 }
5376                 return r;
5377             },
5378
5379             "nth-child" : function(c, a) {
5380                 var r = [], ri = -1;
5381                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5382                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5383                 for(var i = 0, n; n = c[i]; i++){
5384                     var pn = n.parentNode;
5385                     if (batch != pn._batch) {
5386                         var j = 0;
5387                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5388                             if(cn.nodeType == 1){
5389                                cn.nodeIndex = ++j;
5390                             }
5391                         }
5392                         pn._batch = batch;
5393                     }
5394                     if (f == 1) {
5395                         if (l == 0 || n.nodeIndex == l){
5396                             r[++ri] = n;
5397                         }
5398                     } else if ((n.nodeIndex + l) % f == 0){
5399                         r[++ri] = n;
5400                     }
5401                 }
5402
5403                 return r;
5404             },
5405
5406             "only-child" : function(c){
5407                 var r = [], ri = -1;;
5408                 for(var i = 0, ci; ci = c[i]; i++){
5409                     if(!prev(ci) && !next(ci)){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "empty" : function(c){
5417                 var r = [], ri = -1;
5418                 for(var i = 0, ci; ci = c[i]; i++){
5419                     var cns = ci.childNodes, j = 0, cn, empty = true;
5420                     while(cn = cns[j]){
5421                         ++j;
5422                         if(cn.nodeType == 1 || cn.nodeType == 3){
5423                             empty = false;
5424                             break;
5425                         }
5426                     }
5427                     if(empty){
5428                         r[++ri] = ci;
5429                     }
5430                 }
5431                 return r;
5432             },
5433
5434             "contains" : function(c, v){
5435                 var r = [], ri = -1;
5436                 for(var i = 0, ci; ci = c[i]; i++){
5437                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5438                         r[++ri] = ci;
5439                     }
5440                 }
5441                 return r;
5442             },
5443
5444             "nodeValue" : function(c, v){
5445                 var r = [], ri = -1;
5446                 for(var i = 0, ci; ci = c[i]; i++){
5447                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5448                         r[++ri] = ci;
5449                     }
5450                 }
5451                 return r;
5452             },
5453
5454             "checked" : function(c){
5455                 var r = [], ri = -1;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(ci.checked == true){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "not" : function(c, ss){
5465                 return Roo.DomQuery.filter(c, ss, true);
5466             },
5467
5468             "odd" : function(c){
5469                 return this["nth-child"](c, "odd");
5470             },
5471
5472             "even" : function(c){
5473                 return this["nth-child"](c, "even");
5474             },
5475
5476             "nth" : function(c, a){
5477                 return c[a-1] || [];
5478             },
5479
5480             "first" : function(c){
5481                 return c[0] || [];
5482             },
5483
5484             "last" : function(c){
5485                 return c[c.length-1] || [];
5486             },
5487
5488             "has" : function(c, ss){
5489                 var s = Roo.DomQuery.select;
5490                 var r = [], ri = -1;
5491                 for(var i = 0, ci; ci = c[i]; i++){
5492                     if(s(ss, ci).length > 0){
5493                         r[++ri] = ci;
5494                     }
5495                 }
5496                 return r;
5497             },
5498
5499             "next" : function(c, ss){
5500                 var is = Roo.DomQuery.is;
5501                 var r = [], ri = -1;
5502                 for(var i = 0, ci; ci = c[i]; i++){
5503                     var n = next(ci);
5504                     if(n && is(n, ss)){
5505                         r[++ri] = ci;
5506                     }
5507                 }
5508                 return r;
5509             },
5510
5511             "prev" : function(c, ss){
5512                 var is = Roo.DomQuery.is;
5513                 var r = [], ri = -1;
5514                 for(var i = 0, ci; ci = c[i]; i++){
5515                     var n = prev(ci);
5516                     if(n && is(n, ss)){
5517                         r[++ri] = ci;
5518                     }
5519                 }
5520                 return r;
5521             }
5522         }
5523     };
5524 }();
5525
5526 /**
5527  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5528  * @param {String} path The selector/xpath query
5529  * @param {Node} root (optional) The start of the query (defaults to document).
5530  * @return {Array}
5531  * @member Roo
5532  * @method query
5533  */
5534 Roo.query = Roo.DomQuery.select;
5535 /*
5536  * Based on:
5537  * Ext JS Library 1.1.1
5538  * Copyright(c) 2006-2007, Ext JS, LLC.
5539  *
5540  * Originally Released Under LGPL - original licence link has changed is not relivant.
5541  *
5542  * Fork - LGPL
5543  * <script type="text/javascript">
5544  */
5545
5546 /**
5547  * @class Roo.util.Observable
5548  * Base class that provides a common interface for publishing events. Subclasses are expected to
5549  * to have a property "events" with all the events defined.<br>
5550  * For example:
5551  * <pre><code>
5552  Employee = function(name){
5553     this.name = name;
5554     this.addEvents({
5555         "fired" : true,
5556         "quit" : true
5557     });
5558  }
5559  Roo.extend(Employee, Roo.util.Observable);
5560 </code></pre>
5561  * @param {Object} config properties to use (incuding events / listeners)
5562  */
5563
5564 Roo.util.Observable = function(cfg){
5565     
5566     cfg = cfg|| {};
5567     this.addEvents(cfg.events || {});
5568     if (cfg.events) {
5569         delete cfg.events; // make sure
5570     }
5571      
5572     Roo.apply(this, cfg);
5573     
5574     if(this.listeners){
5575         this.on(this.listeners);
5576         delete this.listeners;
5577     }
5578 };
5579 Roo.util.Observable.prototype = {
5580     /** 
5581  * @cfg {Object} listeners  list of events and functions to call for this object, 
5582  * For example :
5583  * <pre><code>
5584     listeners :  { 
5585        'click' : function(e) {
5586            ..... 
5587         } ,
5588         .... 
5589     } 
5590   </code></pre>
5591  */
5592     
5593     
5594     /**
5595      * Fires the specified event with the passed parameters (minus the event name).
5596      * @param {String} eventName
5597      * @param {Object...} args Variable number of parameters are passed to handlers
5598      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5599      */
5600     fireEvent : function(){
5601         var ce = this.events[arguments[0].toLowerCase()];
5602         if(typeof ce == "object"){
5603             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5604         }else{
5605             return true;
5606         }
5607     },
5608
5609     // private
5610     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5611
5612     /**
5613      * Appends an event handler to this component
5614      * @param {String}   eventName The type of event to listen for
5615      * @param {Function} handler The method the event invokes
5616      * @param {Object}   scope (optional) The scope in which to execute the handler
5617      * function. The handler function's "this" context.
5618      * @param {Object}   options (optional) An object containing handler configuration
5619      * properties. This may contain any of the following properties:<ul>
5620      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5621      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5622      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5623      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5624      * by the specified number of milliseconds. If the event fires again within that time, the original
5625      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5626      * </ul><br>
5627      * <p>
5628      * <b>Combining Options</b><br>
5629      * Using the options argument, it is possible to combine different types of listeners:<br>
5630      * <br>
5631      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5632                 <pre><code>
5633                 el.on('click', this.onClick, this, {
5634                         single: true,
5635                 delay: 100,
5636                 forumId: 4
5637                 });
5638                 </code></pre>
5639      * <p>
5640      * <b>Attaching multiple handlers in 1 call</b><br>
5641      * The method also allows for a single argument to be passed which is a config object containing properties
5642      * which specify multiple handlers.
5643      * <pre><code>
5644                 el.on({
5645                         'click': {
5646                         fn: this.onClick,
5647                         scope: this,
5648                         delay: 100
5649                 }, 
5650                 'mouseover': {
5651                         fn: this.onMouseOver,
5652                         scope: this
5653                 },
5654                 'mouseout': {
5655                         fn: this.onMouseOut,
5656                         scope: this
5657                 }
5658                 });
5659                 </code></pre>
5660      * <p>
5661      * Or a shorthand syntax which passes the same scope object to all handlers:
5662         <pre><code>
5663                 el.on({
5664                         'click': this.onClick,
5665                 'mouseover': this.onMouseOver,
5666                 'mouseout': this.onMouseOut,
5667                 scope: this
5668                 });
5669                 </code></pre>
5670      */
5671     addListener : function(eventName, fn, scope, o){
5672         if(typeof eventName == "object"){
5673             o = eventName;
5674             for(var e in o){
5675                 if(this.filterOptRe.test(e)){
5676                     continue;
5677                 }
5678                 if(typeof o[e] == "function"){
5679                     // shared options
5680                     this.addListener(e, o[e], o.scope,  o);
5681                 }else{
5682                     // individual options
5683                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5684                 }
5685             }
5686             return;
5687         }
5688         o = (!o || typeof o == "boolean") ? {} : o;
5689         eventName = eventName.toLowerCase();
5690         var ce = this.events[eventName] || true;
5691         if(typeof ce == "boolean"){
5692             ce = new Roo.util.Event(this, eventName);
5693             this.events[eventName] = ce;
5694         }
5695         ce.addListener(fn, scope, o);
5696     },
5697
5698     /**
5699      * Removes a listener
5700      * @param {String}   eventName     The type of event to listen for
5701      * @param {Function} handler        The handler to remove
5702      * @param {Object}   scope  (optional) The scope (this object) for the handler
5703      */
5704     removeListener : function(eventName, fn, scope){
5705         var ce = this.events[eventName.toLowerCase()];
5706         if(typeof ce == "object"){
5707             ce.removeListener(fn, scope);
5708         }
5709     },
5710
5711     /**
5712      * Removes all listeners for this object
5713      */
5714     purgeListeners : function(){
5715         for(var evt in this.events){
5716             if(typeof this.events[evt] == "object"){
5717                  this.events[evt].clearListeners();
5718             }
5719         }
5720     },
5721
5722     relayEvents : function(o, events){
5723         var createHandler = function(ename){
5724             return function(){
5725                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5726             };
5727         };
5728         for(var i = 0, len = events.length; i < len; i++){
5729             var ename = events[i];
5730             if(!this.events[ename]){ this.events[ename] = true; };
5731             o.on(ename, createHandler(ename), this);
5732         }
5733     },
5734
5735     /**
5736      * Used to define events on this Observable
5737      * @param {Object} object The object with the events defined
5738      */
5739     addEvents : function(o){
5740         if(!this.events){
5741             this.events = {};
5742         }
5743         Roo.applyIf(this.events, o);
5744     },
5745
5746     /**
5747      * Checks to see if this object has any listeners for a specified event
5748      * @param {String} eventName The name of the event to check for
5749      * @return {Boolean} True if the event is being listened for, else false
5750      */
5751     hasListener : function(eventName){
5752         var e = this.events[eventName];
5753         return typeof e == "object" && e.listeners.length > 0;
5754     }
5755 };
5756 /**
5757  * Appends an event handler to this element (shorthand for addListener)
5758  * @param {String}   eventName     The type of event to listen for
5759  * @param {Function} handler        The method the event invokes
5760  * @param {Object}   scope (optional) The scope in which to execute the handler
5761  * function. The handler function's "this" context.
5762  * @param {Object}   options  (optional)
5763  * @method
5764  */
5765 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5766 /**
5767  * Removes a listener (shorthand for removeListener)
5768  * @param {String}   eventName     The type of event to listen for
5769  * @param {Function} handler        The handler to remove
5770  * @param {Object}   scope  (optional) The scope (this object) for the handler
5771  * @method
5772  */
5773 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5774
5775 /**
5776  * Starts capture on the specified Observable. All events will be passed
5777  * to the supplied function with the event name + standard signature of the event
5778  * <b>before</b> the event is fired. If the supplied function returns false,
5779  * the event will not fire.
5780  * @param {Observable} o The Observable to capture
5781  * @param {Function} fn The function to call
5782  * @param {Object} scope (optional) The scope (this object) for the fn
5783  * @static
5784  */
5785 Roo.util.Observable.capture = function(o, fn, scope){
5786     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5787 };
5788
5789 /**
5790  * Removes <b>all</b> added captures from the Observable.
5791  * @param {Observable} o The Observable to release
5792  * @static
5793  */
5794 Roo.util.Observable.releaseCapture = function(o){
5795     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5796 };
5797
5798 (function(){
5799
5800     var createBuffered = function(h, o, scope){
5801         var task = new Roo.util.DelayedTask();
5802         return function(){
5803             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5804         };
5805     };
5806
5807     var createSingle = function(h, e, fn, scope){
5808         return function(){
5809             e.removeListener(fn, scope);
5810             return h.apply(scope, arguments);
5811         };
5812     };
5813
5814     var createDelayed = function(h, o, scope){
5815         return function(){
5816             var args = Array.prototype.slice.call(arguments, 0);
5817             setTimeout(function(){
5818                 h.apply(scope, args);
5819             }, o.delay || 10);
5820         };
5821     };
5822
5823     Roo.util.Event = function(obj, name){
5824         this.name = name;
5825         this.obj = obj;
5826         this.listeners = [];
5827     };
5828
5829     Roo.util.Event.prototype = {
5830         addListener : function(fn, scope, options){
5831             var o = options || {};
5832             scope = scope || this.obj;
5833             if(!this.isListening(fn, scope)){
5834                 var l = {fn: fn, scope: scope, options: o};
5835                 var h = fn;
5836                 if(o.delay){
5837                     h = createDelayed(h, o, scope);
5838                 }
5839                 if(o.single){
5840                     h = createSingle(h, this, fn, scope);
5841                 }
5842                 if(o.buffer){
5843                     h = createBuffered(h, o, scope);
5844                 }
5845                 l.fireFn = h;
5846                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5847                     this.listeners.push(l);
5848                 }else{
5849                     this.listeners = this.listeners.slice(0);
5850                     this.listeners.push(l);
5851                 }
5852             }
5853         },
5854
5855         findListener : function(fn, scope){
5856             scope = scope || this.obj;
5857             var ls = this.listeners;
5858             for(var i = 0, len = ls.length; i < len; i++){
5859                 var l = ls[i];
5860                 if(l.fn == fn && l.scope == scope){
5861                     return i;
5862                 }
5863             }
5864             return -1;
5865         },
5866
5867         isListening : function(fn, scope){
5868             return this.findListener(fn, scope) != -1;
5869         },
5870
5871         removeListener : function(fn, scope){
5872             var index;
5873             if((index = this.findListener(fn, scope)) != -1){
5874                 if(!this.firing){
5875                     this.listeners.splice(index, 1);
5876                 }else{
5877                     this.listeners = this.listeners.slice(0);
5878                     this.listeners.splice(index, 1);
5879                 }
5880                 return true;
5881             }
5882             return false;
5883         },
5884
5885         clearListeners : function(){
5886             this.listeners = [];
5887         },
5888
5889         fire : function(){
5890             var ls = this.listeners, scope, len = ls.length;
5891             if(len > 0){
5892                 this.firing = true;
5893                 var args = Array.prototype.slice.call(arguments, 0);
5894                 for(var i = 0; i < len; i++){
5895                     var l = ls[i];
5896                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5897                         this.firing = false;
5898                         return false;
5899                     }
5900                 }
5901                 this.firing = false;
5902             }
5903             return true;
5904         }
5905     };
5906 })();/*
5907  * Based on:
5908  * Ext JS Library 1.1.1
5909  * Copyright(c) 2006-2007, Ext JS, LLC.
5910  *
5911  * Originally Released Under LGPL - original licence link has changed is not relivant.
5912  *
5913  * Fork - LGPL
5914  * <script type="text/javascript">
5915  */
5916
5917 /**
5918  * @class Roo.EventManager
5919  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5920  * several useful events directly.
5921  * See {@link Roo.EventObject} for more details on normalized event objects.
5922  * @singleton
5923  */
5924 Roo.EventManager = function(){
5925     var docReadyEvent, docReadyProcId, docReadyState = false;
5926     var resizeEvent, resizeTask, textEvent, textSize;
5927     var E = Roo.lib.Event;
5928     var D = Roo.lib.Dom;
5929
5930
5931     var fireDocReady = function(){
5932         if(!docReadyState){
5933             docReadyState = true;
5934             Roo.isReady = true;
5935             if(docReadyProcId){
5936                 clearInterval(docReadyProcId);
5937             }
5938             if(Roo.isGecko || Roo.isOpera) {
5939                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5940             }
5941             if(Roo.isIE){
5942                 var defer = document.getElementById("ie-deferred-loader");
5943                 if(defer){
5944                     defer.onreadystatechange = null;
5945                     defer.parentNode.removeChild(defer);
5946                 }
5947             }
5948             if(docReadyEvent){
5949                 docReadyEvent.fire();
5950                 docReadyEvent.clearListeners();
5951             }
5952         }
5953     };
5954     
5955     var initDocReady = function(){
5956         docReadyEvent = new Roo.util.Event();
5957         if(Roo.isGecko || Roo.isOpera) {
5958             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5959         }else if(Roo.isIE){
5960             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5961             var defer = document.getElementById("ie-deferred-loader");
5962             defer.onreadystatechange = function(){
5963                 if(this.readyState == "complete"){
5964                     fireDocReady();
5965                 }
5966             };
5967         }else if(Roo.isSafari){ 
5968             docReadyProcId = setInterval(function(){
5969                 var rs = document.readyState;
5970                 if(rs == "complete") {
5971                     fireDocReady();     
5972                  }
5973             }, 10);
5974         }
5975         // no matter what, make sure it fires on load
5976         E.on(window, "load", fireDocReady);
5977     };
5978
5979     var createBuffered = function(h, o){
5980         var task = new Roo.util.DelayedTask(h);
5981         return function(e){
5982             // create new event object impl so new events don't wipe out properties
5983             e = new Roo.EventObjectImpl(e);
5984             task.delay(o.buffer, h, null, [e]);
5985         };
5986     };
5987
5988     var createSingle = function(h, el, ename, fn){
5989         return function(e){
5990             Roo.EventManager.removeListener(el, ename, fn);
5991             h(e);
5992         };
5993     };
5994
5995     var createDelayed = function(h, o){
5996         return function(e){
5997             // create new event object impl so new events don't wipe out properties
5998             e = new Roo.EventObjectImpl(e);
5999             setTimeout(function(){
6000                 h(e);
6001             }, o.delay || 10);
6002         };
6003     };
6004
6005     var listen = function(element, ename, opt, fn, scope){
6006         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6007         fn = fn || o.fn; scope = scope || o.scope;
6008         var el = Roo.getDom(element);
6009         if(!el){
6010             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6011         }
6012         var h = function(e){
6013             e = Roo.EventObject.setEvent(e);
6014             var t;
6015             if(o.delegate){
6016                 t = e.getTarget(o.delegate, el);
6017                 if(!t){
6018                     return;
6019                 }
6020             }else{
6021                 t = e.target;
6022             }
6023             if(o.stopEvent === true){
6024                 e.stopEvent();
6025             }
6026             if(o.preventDefault === true){
6027                e.preventDefault();
6028             }
6029             if(o.stopPropagation === true){
6030                 e.stopPropagation();
6031             }
6032
6033             if(o.normalized === false){
6034                 e = e.browserEvent;
6035             }
6036
6037             fn.call(scope || el, e, t, o);
6038         };
6039         if(o.delay){
6040             h = createDelayed(h, o);
6041         }
6042         if(o.single){
6043             h = createSingle(h, el, ename, fn);
6044         }
6045         if(o.buffer){
6046             h = createBuffered(h, o);
6047         }
6048         fn._handlers = fn._handlers || [];
6049         fn._handlers.push([Roo.id(el), ename, h]);
6050
6051         E.on(el, ename, h);
6052         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6053             el.addEventListener("DOMMouseScroll", h, false);
6054             E.on(window, 'unload', function(){
6055                 el.removeEventListener("DOMMouseScroll", h, false);
6056             });
6057         }
6058         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6059             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6060         }
6061         return h;
6062     };
6063
6064     var stopListening = function(el, ename, fn){
6065         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6066         if(hds){
6067             for(var i = 0, len = hds.length; i < len; i++){
6068                 var h = hds[i];
6069                 if(h[0] == id && h[1] == ename){
6070                     hd = h[2];
6071                     hds.splice(i, 1);
6072                     break;
6073                 }
6074             }
6075         }
6076         E.un(el, ename, hd);
6077         el = Roo.getDom(el);
6078         if(ename == "mousewheel" && el.addEventListener){
6079             el.removeEventListener("DOMMouseScroll", hd, false);
6080         }
6081         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6082             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6083         }
6084     };
6085
6086     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6087     
6088     var pub = {
6089         
6090         
6091         /** 
6092          * Fix for doc tools
6093          * @scope Roo.EventManager
6094          */
6095         
6096         
6097         /** 
6098          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6099          * object with a Roo.EventObject
6100          * @param {Function} fn        The method the event invokes
6101          * @param {Object}   scope    An object that becomes the scope of the handler
6102          * @param {boolean}  override If true, the obj passed in becomes
6103          *                             the execution scope of the listener
6104          * @return {Function} The wrapped function
6105          * @deprecated
6106          */
6107         wrap : function(fn, scope, override){
6108             return function(e){
6109                 Roo.EventObject.setEvent(e);
6110                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6111             };
6112         },
6113         
6114         /**
6115      * Appends an event handler to an element (shorthand for addListener)
6116      * @param {String/HTMLElement}   element        The html element or id to assign the
6117      * @param {String}   eventName The type of event to listen for
6118      * @param {Function} handler The method the event invokes
6119      * @param {Object}   scope (optional) The scope in which to execute the handler
6120      * function. The handler function's "this" context.
6121      * @param {Object}   options (optional) An object containing handler configuration
6122      * properties. This may contain any of the following properties:<ul>
6123      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6124      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6125      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6126      * <li>preventDefault {Boolean} True to prevent the default action</li>
6127      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6128      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6129      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6130      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6131      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6132      * by the specified number of milliseconds. If the event fires again within that time, the original
6133      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6134      * </ul><br>
6135      * <p>
6136      * <b>Combining Options</b><br>
6137      * Using the options argument, it is possible to combine different types of listeners:<br>
6138      * <br>
6139      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6140      * Code:<pre><code>
6141 el.on('click', this.onClick, this, {
6142     single: true,
6143     delay: 100,
6144     stopEvent : true,
6145     forumId: 4
6146 });</code></pre>
6147      * <p>
6148      * <b>Attaching multiple handlers in 1 call</b><br>
6149       * The method also allows for a single argument to be passed which is a config object containing properties
6150      * which specify multiple handlers.
6151      * <p>
6152      * Code:<pre><code>
6153 el.on({
6154     'click' : {
6155         fn: this.onClick
6156         scope: this,
6157         delay: 100
6158     },
6159     'mouseover' : {
6160         fn: this.onMouseOver
6161         scope: this
6162     },
6163     'mouseout' : {
6164         fn: this.onMouseOut
6165         scope: this
6166     }
6167 });</code></pre>
6168      * <p>
6169      * Or a shorthand syntax:<br>
6170      * Code:<pre><code>
6171 el.on({
6172     'click' : this.onClick,
6173     'mouseover' : this.onMouseOver,
6174     'mouseout' : this.onMouseOut
6175     scope: this
6176 });</code></pre>
6177      */
6178         addListener : function(element, eventName, fn, scope, options){
6179             if(typeof eventName == "object"){
6180                 var o = eventName;
6181                 for(var e in o){
6182                     if(propRe.test(e)){
6183                         continue;
6184                     }
6185                     if(typeof o[e] == "function"){
6186                         // shared options
6187                         listen(element, e, o, o[e], o.scope);
6188                     }else{
6189                         // individual options
6190                         listen(element, e, o[e]);
6191                     }
6192                 }
6193                 return;
6194             }
6195             return listen(element, eventName, options, fn, scope);
6196         },
6197         
6198         /**
6199          * Removes an event handler
6200          *
6201          * @param {String/HTMLElement}   element        The id or html element to remove the 
6202          *                             event from
6203          * @param {String}   eventName     The type of event
6204          * @param {Function} fn
6205          * @return {Boolean} True if a listener was actually removed
6206          */
6207         removeListener : function(element, eventName, fn){
6208             return stopListening(element, eventName, fn);
6209         },
6210         
6211         /**
6212          * Fires when the document is ready (before onload and before images are loaded). Can be 
6213          * accessed shorthanded Roo.onReady().
6214          * @param {Function} fn        The method the event invokes
6215          * @param {Object}   scope    An  object that becomes the scope of the handler
6216          * @param {boolean}  options
6217          */
6218         onDocumentReady : function(fn, scope, options){
6219             if(docReadyState){ // if it already fired
6220                 docReadyEvent.addListener(fn, scope, options);
6221                 docReadyEvent.fire();
6222                 docReadyEvent.clearListeners();
6223                 return;
6224             }
6225             if(!docReadyEvent){
6226                 initDocReady();
6227             }
6228             docReadyEvent.addListener(fn, scope, options);
6229         },
6230         
6231         /**
6232          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6233          * @param {Function} fn        The method the event invokes
6234          * @param {Object}   scope    An object that becomes the scope of the handler
6235          * @param {boolean}  options
6236          */
6237         onWindowResize : function(fn, scope, options){
6238             if(!resizeEvent){
6239                 resizeEvent = new Roo.util.Event();
6240                 resizeTask = new Roo.util.DelayedTask(function(){
6241                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6242                 });
6243                 E.on(window, "resize", function(){
6244                     if(Roo.isIE){
6245                         resizeTask.delay(50);
6246                     }else{
6247                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6248                     }
6249                 });
6250             }
6251             resizeEvent.addListener(fn, scope, options);
6252         },
6253
6254         /**
6255          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6256          * @param {Function} fn        The method the event invokes
6257          * @param {Object}   scope    An object that becomes the scope of the handler
6258          * @param {boolean}  options
6259          */
6260         onTextResize : function(fn, scope, options){
6261             if(!textEvent){
6262                 textEvent = new Roo.util.Event();
6263                 var textEl = new Roo.Element(document.createElement('div'));
6264                 textEl.dom.className = 'x-text-resize';
6265                 textEl.dom.innerHTML = 'X';
6266                 textEl.appendTo(document.body);
6267                 textSize = textEl.dom.offsetHeight;
6268                 setInterval(function(){
6269                     if(textEl.dom.offsetHeight != textSize){
6270                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6271                     }
6272                 }, this.textResizeInterval);
6273             }
6274             textEvent.addListener(fn, scope, options);
6275         },
6276
6277         /**
6278          * Removes the passed window resize listener.
6279          * @param {Function} fn        The method the event invokes
6280          * @param {Object}   scope    The scope of handler
6281          */
6282         removeResizeListener : function(fn, scope){
6283             if(resizeEvent){
6284                 resizeEvent.removeListener(fn, scope);
6285             }
6286         },
6287
6288         // private
6289         fireResize : function(){
6290             if(resizeEvent){
6291                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6292             }   
6293         },
6294         /**
6295          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6296          */
6297         ieDeferSrc : false,
6298         /**
6299          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6300          */
6301         textResizeInterval : 50
6302     };
6303     
6304     /**
6305      * Fix for doc tools
6306      * @scopeAlias pub=Roo.EventManager
6307      */
6308     
6309      /**
6310      * Appends an event handler to an element (shorthand for addListener)
6311      * @param {String/HTMLElement}   element        The html element or id to assign the
6312      * @param {String}   eventName The type of event to listen for
6313      * @param {Function} handler The method the event invokes
6314      * @param {Object}   scope (optional) The scope in which to execute the handler
6315      * function. The handler function's "this" context.
6316      * @param {Object}   options (optional) An object containing handler configuration
6317      * properties. This may contain any of the following properties:<ul>
6318      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6319      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6320      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6321      * <li>preventDefault {Boolean} True to prevent the default action</li>
6322      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6323      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6324      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6325      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6326      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6327      * by the specified number of milliseconds. If the event fires again within that time, the original
6328      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6329      * </ul><br>
6330      * <p>
6331      * <b>Combining Options</b><br>
6332      * Using the options argument, it is possible to combine different types of listeners:<br>
6333      * <br>
6334      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6335      * Code:<pre><code>
6336 el.on('click', this.onClick, this, {
6337     single: true,
6338     delay: 100,
6339     stopEvent : true,
6340     forumId: 4
6341 });</code></pre>
6342      * <p>
6343      * <b>Attaching multiple handlers in 1 call</b><br>
6344       * The method also allows for a single argument to be passed which is a config object containing properties
6345      * which specify multiple handlers.
6346      * <p>
6347      * Code:<pre><code>
6348 el.on({
6349     'click' : {
6350         fn: this.onClick
6351         scope: this,
6352         delay: 100
6353     },
6354     'mouseover' : {
6355         fn: this.onMouseOver
6356         scope: this
6357     },
6358     'mouseout' : {
6359         fn: this.onMouseOut
6360         scope: this
6361     }
6362 });</code></pre>
6363      * <p>
6364      * Or a shorthand syntax:<br>
6365      * Code:<pre><code>
6366 el.on({
6367     'click' : this.onClick,
6368     'mouseover' : this.onMouseOver,
6369     'mouseout' : this.onMouseOut
6370     scope: this
6371 });</code></pre>
6372      */
6373     pub.on = pub.addListener;
6374     pub.un = pub.removeListener;
6375
6376     pub.stoppedMouseDownEvent = new Roo.util.Event();
6377     return pub;
6378 }();
6379 /**
6380   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6381   * @param {Function} fn        The method the event invokes
6382   * @param {Object}   scope    An  object that becomes the scope of the handler
6383   * @param {boolean}  override If true, the obj passed in becomes
6384   *                             the execution scope of the listener
6385   * @member Roo
6386   * @method onReady
6387  */
6388 Roo.onReady = Roo.EventManager.onDocumentReady;
6389
6390 Roo.onReady(function(){
6391     var bd = Roo.get(document.body);
6392     if(!bd){ return; }
6393
6394     var cls = [
6395             Roo.isIE ? "roo-ie"
6396             : Roo.isGecko ? "roo-gecko"
6397             : Roo.isOpera ? "roo-opera"
6398             : Roo.isSafari ? "roo-safari" : ""];
6399
6400     if(Roo.isMac){
6401         cls.push("roo-mac");
6402     }
6403     if(Roo.isLinux){
6404         cls.push("roo-linux");
6405     }
6406     if(Roo.isBorderBox){
6407         cls.push('roo-border-box');
6408     }
6409     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6410         var p = bd.dom.parentNode;
6411         if(p){
6412             p.className += ' roo-strict';
6413         }
6414     }
6415     bd.addClass(cls.join(' '));
6416 });
6417
6418 /**
6419  * @class Roo.EventObject
6420  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6421  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6422  * Example:
6423  * <pre><code>
6424  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6425     e.preventDefault();
6426     var target = e.getTarget();
6427     ...
6428  }
6429  var myDiv = Roo.get("myDiv");
6430  myDiv.on("click", handleClick);
6431  //or
6432  Roo.EventManager.on("myDiv", 'click', handleClick);
6433  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6434  </code></pre>
6435  * @singleton
6436  */
6437 Roo.EventObject = function(){
6438     
6439     var E = Roo.lib.Event;
6440     
6441     // safari keypress events for special keys return bad keycodes
6442     var safariKeys = {
6443         63234 : 37, // left
6444         63235 : 39, // right
6445         63232 : 38, // up
6446         63233 : 40, // down
6447         63276 : 33, // page up
6448         63277 : 34, // page down
6449         63272 : 46, // delete
6450         63273 : 36, // home
6451         63275 : 35  // end
6452     };
6453
6454     // normalize button clicks
6455     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6456                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6457
6458     Roo.EventObjectImpl = function(e){
6459         if(e){
6460             this.setEvent(e.browserEvent || e);
6461         }
6462     };
6463     Roo.EventObjectImpl.prototype = {
6464         /**
6465          * Used to fix doc tools.
6466          * @scope Roo.EventObject.prototype
6467          */
6468             
6469
6470         
6471         
6472         /** The normal browser event */
6473         browserEvent : null,
6474         /** The button pressed in a mouse event */
6475         button : -1,
6476         /** True if the shift key was down during the event */
6477         shiftKey : false,
6478         /** True if the control key was down during the event */
6479         ctrlKey : false,
6480         /** True if the alt key was down during the event */
6481         altKey : false,
6482
6483         /** Key constant 
6484         * @type Number */
6485         BACKSPACE : 8,
6486         /** Key constant 
6487         * @type Number */
6488         TAB : 9,
6489         /** Key constant 
6490         * @type Number */
6491         RETURN : 13,
6492         /** Key constant 
6493         * @type Number */
6494         ENTER : 13,
6495         /** Key constant 
6496         * @type Number */
6497         SHIFT : 16,
6498         /** Key constant 
6499         * @type Number */
6500         CONTROL : 17,
6501         /** Key constant 
6502         * @type Number */
6503         ESC : 27,
6504         /** Key constant 
6505         * @type Number */
6506         SPACE : 32,
6507         /** Key constant 
6508         * @type Number */
6509         PAGEUP : 33,
6510         /** Key constant 
6511         * @type Number */
6512         PAGEDOWN : 34,
6513         /** Key constant 
6514         * @type Number */
6515         END : 35,
6516         /** Key constant 
6517         * @type Number */
6518         HOME : 36,
6519         /** Key constant 
6520         * @type Number */
6521         LEFT : 37,
6522         /** Key constant 
6523         * @type Number */
6524         UP : 38,
6525         /** Key constant 
6526         * @type Number */
6527         RIGHT : 39,
6528         /** Key constant 
6529         * @type Number */
6530         DOWN : 40,
6531         /** Key constant 
6532         * @type Number */
6533         DELETE : 46,
6534         /** Key constant 
6535         * @type Number */
6536         F5 : 116,
6537
6538            /** @private */
6539         setEvent : function(e){
6540             if(e == this || (e && e.browserEvent)){ // already wrapped
6541                 return e;
6542             }
6543             this.browserEvent = e;
6544             if(e){
6545                 // normalize buttons
6546                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6547                 if(e.type == 'click' && this.button == -1){
6548                     this.button = 0;
6549                 }
6550                 this.type = e.type;
6551                 this.shiftKey = e.shiftKey;
6552                 // mac metaKey behaves like ctrlKey
6553                 this.ctrlKey = e.ctrlKey || e.metaKey;
6554                 this.altKey = e.altKey;
6555                 // in getKey these will be normalized for the mac
6556                 this.keyCode = e.keyCode;
6557                 // keyup warnings on firefox.
6558                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6559                 // cache the target for the delayed and or buffered events
6560                 this.target = E.getTarget(e);
6561                 // same for XY
6562                 this.xy = E.getXY(e);
6563             }else{
6564                 this.button = -1;
6565                 this.shiftKey = false;
6566                 this.ctrlKey = false;
6567                 this.altKey = false;
6568                 this.keyCode = 0;
6569                 this.charCode =0;
6570                 this.target = null;
6571                 this.xy = [0, 0];
6572             }
6573             return this;
6574         },
6575
6576         /**
6577          * Stop the event (preventDefault and stopPropagation)
6578          */
6579         stopEvent : function(){
6580             if(this.browserEvent){
6581                 if(this.browserEvent.type == 'mousedown'){
6582                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6583                 }
6584                 E.stopEvent(this.browserEvent);
6585             }
6586         },
6587
6588         /**
6589          * Prevents the browsers default handling of the event.
6590          */
6591         preventDefault : function(){
6592             if(this.browserEvent){
6593                 E.preventDefault(this.browserEvent);
6594             }
6595         },
6596
6597         /** @private */
6598         isNavKeyPress : function(){
6599             var k = this.keyCode;
6600             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6601             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6602         },
6603
6604         isSpecialKey : function(){
6605             var k = this.keyCode;
6606             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6607             (k == 16) || (k == 17) ||
6608             (k >= 18 && k <= 20) ||
6609             (k >= 33 && k <= 35) ||
6610             (k >= 36 && k <= 39) ||
6611             (k >= 44 && k <= 45);
6612         },
6613         /**
6614          * Cancels bubbling of the event.
6615          */
6616         stopPropagation : function(){
6617             if(this.browserEvent){
6618                 if(this.type == 'mousedown'){
6619                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6620                 }
6621                 E.stopPropagation(this.browserEvent);
6622             }
6623         },
6624
6625         /**
6626          * Gets the key code for the event.
6627          * @return {Number}
6628          */
6629         getCharCode : function(){
6630             return this.charCode || this.keyCode;
6631         },
6632
6633         /**
6634          * Returns a normalized keyCode for the event.
6635          * @return {Number} The key code
6636          */
6637         getKey : function(){
6638             var k = this.keyCode || this.charCode;
6639             return Roo.isSafari ? (safariKeys[k] || k) : k;
6640         },
6641
6642         /**
6643          * Gets the x coordinate of the event.
6644          * @return {Number}
6645          */
6646         getPageX : function(){
6647             return this.xy[0];
6648         },
6649
6650         /**
6651          * Gets the y coordinate of the event.
6652          * @return {Number}
6653          */
6654         getPageY : function(){
6655             return this.xy[1];
6656         },
6657
6658         /**
6659          * Gets the time of the event.
6660          * @return {Number}
6661          */
6662         getTime : function(){
6663             if(this.browserEvent){
6664                 return E.getTime(this.browserEvent);
6665             }
6666             return null;
6667         },
6668
6669         /**
6670          * Gets the page coordinates of the event.
6671          * @return {Array} The xy values like [x, y]
6672          */
6673         getXY : function(){
6674             return this.xy;
6675         },
6676
6677         /**
6678          * Gets the target for the event.
6679          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6680          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6681                 search as a number or element (defaults to 10 || document.body)
6682          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6683          * @return {HTMLelement}
6684          */
6685         getTarget : function(selector, maxDepth, returnEl){
6686             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6687         },
6688         /**
6689          * Gets the related target.
6690          * @return {HTMLElement}
6691          */
6692         getRelatedTarget : function(){
6693             if(this.browserEvent){
6694                 return E.getRelatedTarget(this.browserEvent);
6695             }
6696             return null;
6697         },
6698
6699         /**
6700          * Normalizes mouse wheel delta across browsers
6701          * @return {Number} The delta
6702          */
6703         getWheelDelta : function(){
6704             var e = this.browserEvent;
6705             var delta = 0;
6706             if(e.wheelDelta){ /* IE/Opera. */
6707                 delta = e.wheelDelta/120;
6708             }else if(e.detail){ /* Mozilla case. */
6709                 delta = -e.detail/3;
6710             }
6711             return delta;
6712         },
6713
6714         /**
6715          * Returns true if the control, meta, shift or alt key was pressed during this event.
6716          * @return {Boolean}
6717          */
6718         hasModifier : function(){
6719             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6720         },
6721
6722         /**
6723          * Returns true if the target of this event equals el or is a child of el
6724          * @param {String/HTMLElement/Element} el
6725          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6726          * @return {Boolean}
6727          */
6728         within : function(el, related){
6729             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6730             return t && Roo.fly(el).contains(t);
6731         },
6732
6733         getPoint : function(){
6734             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6735         }
6736     };
6737
6738     return new Roo.EventObjectImpl();
6739 }();
6740             
6741     /*
6742  * Based on:
6743  * Ext JS Library 1.1.1
6744  * Copyright(c) 2006-2007, Ext JS, LLC.
6745  *
6746  * Originally Released Under LGPL - original licence link has changed is not relivant.
6747  *
6748  * Fork - LGPL
6749  * <script type="text/javascript">
6750  */
6751
6752  
6753 // was in Composite Element!??!?!
6754  
6755 (function(){
6756     var D = Roo.lib.Dom;
6757     var E = Roo.lib.Event;
6758     var A = Roo.lib.Anim;
6759
6760     // local style camelizing for speed
6761     var propCache = {};
6762     var camelRe = /(-[a-z])/gi;
6763     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6764     var view = document.defaultView;
6765
6766 /**
6767  * @class Roo.Element
6768  * Represents an Element in the DOM.<br><br>
6769  * Usage:<br>
6770 <pre><code>
6771 var el = Roo.get("my-div");
6772
6773 // or with getEl
6774 var el = getEl("my-div");
6775
6776 // or with a DOM element
6777 var el = Roo.get(myDivElement);
6778 </code></pre>
6779  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6780  * each call instead of constructing a new one.<br><br>
6781  * <b>Animations</b><br />
6782  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6783  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6784 <pre>
6785 Option    Default   Description
6786 --------- --------  ---------------------------------------------
6787 duration  .35       The duration of the animation in seconds
6788 easing    easeOut   The YUI easing method
6789 callback  none      A function to execute when the anim completes
6790 scope     this      The scope (this) of the callback function
6791 </pre>
6792 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6793 * manipulate the animation. Here's an example:
6794 <pre><code>
6795 var el = Roo.get("my-div");
6796
6797 // no animation
6798 el.setWidth(100);
6799
6800 // default animation
6801 el.setWidth(100, true);
6802
6803 // animation with some options set
6804 el.setWidth(100, {
6805     duration: 1,
6806     callback: this.foo,
6807     scope: this
6808 });
6809
6810 // using the "anim" property to get the Anim object
6811 var opt = {
6812     duration: 1,
6813     callback: this.foo,
6814     scope: this
6815 };
6816 el.setWidth(100, opt);
6817 ...
6818 if(opt.anim.isAnimated()){
6819     opt.anim.stop();
6820 }
6821 </code></pre>
6822 * <b> Composite (Collections of) Elements</b><br />
6823  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6824  * @constructor Create a new Element directly.
6825  * @param {String/HTMLElement} element
6826  * @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).
6827  */
6828     Roo.Element = function(element, forceNew){
6829         var dom = typeof element == "string" ?
6830                 document.getElementById(element) : element;
6831         if(!dom){ // invalid id/element
6832             return null;
6833         }
6834         var id = dom.id;
6835         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6836             return Roo.Element.cache[id];
6837         }
6838
6839         /**
6840          * The DOM element
6841          * @type HTMLElement
6842          */
6843         this.dom = dom;
6844
6845         /**
6846          * The DOM element ID
6847          * @type String
6848          */
6849         this.id = id || Roo.id(dom);
6850     };
6851
6852     var El = Roo.Element;
6853
6854     El.prototype = {
6855         /**
6856          * The element's default display mode  (defaults to "")
6857          * @type String
6858          */
6859         originalDisplay : "",
6860
6861         visibilityMode : 1,
6862         /**
6863          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6864          * @type String
6865          */
6866         defaultUnit : "px",
6867         /**
6868          * Sets the element's visibility mode. When setVisible() is called it
6869          * will use this to determine whether to set the visibility or the display property.
6870          * @param visMode Element.VISIBILITY or Element.DISPLAY
6871          * @return {Roo.Element} this
6872          */
6873         setVisibilityMode : function(visMode){
6874             this.visibilityMode = visMode;
6875             return this;
6876         },
6877         /**
6878          * Convenience method for setVisibilityMode(Element.DISPLAY)
6879          * @param {String} display (optional) What to set display to when visible
6880          * @return {Roo.Element} this
6881          */
6882         enableDisplayMode : function(display){
6883             this.setVisibilityMode(El.DISPLAY);
6884             if(typeof display != "undefined") this.originalDisplay = display;
6885             return this;
6886         },
6887
6888         /**
6889          * 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)
6890          * @param {String} selector The simple selector to test
6891          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6892                 search as a number or element (defaults to 10 || document.body)
6893          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6894          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6895          */
6896         findParent : function(simpleSelector, maxDepth, returnEl){
6897             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6898             maxDepth = maxDepth || 50;
6899             if(typeof maxDepth != "number"){
6900                 stopEl = Roo.getDom(maxDepth);
6901                 maxDepth = 10;
6902             }
6903             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6904                 if(dq.is(p, simpleSelector)){
6905                     return returnEl ? Roo.get(p) : p;
6906                 }
6907                 depth++;
6908                 p = p.parentNode;
6909             }
6910             return null;
6911         },
6912
6913
6914         /**
6915          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6916          * @param {String} selector The simple selector to test
6917          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6918                 search as a number or element (defaults to 10 || document.body)
6919          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6920          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6921          */
6922         findParentNode : function(simpleSelector, maxDepth, returnEl){
6923             var p = Roo.fly(this.dom.parentNode, '_internal');
6924             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6925         },
6926
6927         /**
6928          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6929          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6930          * @param {String} selector The simple selector to test
6931          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6932                 search as a number or element (defaults to 10 || document.body)
6933          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6934          */
6935         up : function(simpleSelector, maxDepth){
6936             return this.findParentNode(simpleSelector, maxDepth, true);
6937         },
6938
6939
6940
6941         /**
6942          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6943          * @param {String} selector The simple selector to test
6944          * @return {Boolean} True if this element matches the selector, else false
6945          */
6946         is : function(simpleSelector){
6947             return Roo.DomQuery.is(this.dom, simpleSelector);
6948         },
6949
6950         /**
6951          * Perform animation on this element.
6952          * @param {Object} args The YUI animation control args
6953          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6954          * @param {Function} onComplete (optional) Function to call when animation completes
6955          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6956          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6957          * @return {Roo.Element} this
6958          */
6959         animate : function(args, duration, onComplete, easing, animType){
6960             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6961             return this;
6962         },
6963
6964         /*
6965          * @private Internal animation call
6966          */
6967         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6968             animType = animType || 'run';
6969             opt = opt || {};
6970             var anim = Roo.lib.Anim[animType](
6971                 this.dom, args,
6972                 (opt.duration || defaultDur) || .35,
6973                 (opt.easing || defaultEase) || 'easeOut',
6974                 function(){
6975                     Roo.callback(cb, this);
6976                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6977                 },
6978                 this
6979             );
6980             opt.anim = anim;
6981             return anim;
6982         },
6983
6984         // private legacy anim prep
6985         preanim : function(a, i){
6986             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6987         },
6988
6989         /**
6990          * Removes worthless text nodes
6991          * @param {Boolean} forceReclean (optional) By default the element
6992          * keeps track if it has been cleaned already so
6993          * you can call this over and over. However, if you update the element and
6994          * need to force a reclean, you can pass true.
6995          */
6996         clean : function(forceReclean){
6997             if(this.isCleaned && forceReclean !== true){
6998                 return this;
6999             }
7000             var ns = /\S/;
7001             var d = this.dom, n = d.firstChild, ni = -1;
7002             while(n){
7003                 var nx = n.nextSibling;
7004                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7005                     d.removeChild(n);
7006                 }else{
7007                     n.nodeIndex = ++ni;
7008                 }
7009                 n = nx;
7010             }
7011             this.isCleaned = true;
7012             return this;
7013         },
7014
7015         // private
7016         calcOffsetsTo : function(el){
7017             el = Roo.get(el);
7018             var d = el.dom;
7019             var restorePos = false;
7020             if(el.getStyle('position') == 'static'){
7021                 el.position('relative');
7022                 restorePos = true;
7023             }
7024             var x = 0, y =0;
7025             var op = this.dom;
7026             while(op && op != d && op.tagName != 'HTML'){
7027                 x+= op.offsetLeft;
7028                 y+= op.offsetTop;
7029                 op = op.offsetParent;
7030             }
7031             if(restorePos){
7032                 el.position('static');
7033             }
7034             return [x, y];
7035         },
7036
7037         /**
7038          * Scrolls this element into view within the passed container.
7039          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7040          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7041          * @return {Roo.Element} this
7042          */
7043         scrollIntoView : function(container, hscroll){
7044             var c = Roo.getDom(container) || document.body;
7045             var el = this.dom;
7046
7047             var o = this.calcOffsetsTo(c),
7048                 l = o[0],
7049                 t = o[1],
7050                 b = t+el.offsetHeight,
7051                 r = l+el.offsetWidth;
7052
7053             var ch = c.clientHeight;
7054             var ct = parseInt(c.scrollTop, 10);
7055             var cl = parseInt(c.scrollLeft, 10);
7056             var cb = ct + ch;
7057             var cr = cl + c.clientWidth;
7058
7059             if(t < ct){
7060                 c.scrollTop = t;
7061             }else if(b > cb){
7062                 c.scrollTop = b-ch;
7063             }
7064
7065             if(hscroll !== false){
7066                 if(l < cl){
7067                     c.scrollLeft = l;
7068                 }else if(r > cr){
7069                     c.scrollLeft = r-c.clientWidth;
7070                 }
7071             }
7072             return this;
7073         },
7074
7075         // private
7076         scrollChildIntoView : function(child, hscroll){
7077             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7078         },
7079
7080         /**
7081          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7082          * the new height may not be available immediately.
7083          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7084          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7085          * @param {Function} onComplete (optional) Function to call when animation completes
7086          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7087          * @return {Roo.Element} this
7088          */
7089         autoHeight : function(animate, duration, onComplete, easing){
7090             var oldHeight = this.getHeight();
7091             this.clip();
7092             this.setHeight(1); // force clipping
7093             setTimeout(function(){
7094                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7095                 if(!animate){
7096                     this.setHeight(height);
7097                     this.unclip();
7098                     if(typeof onComplete == "function"){
7099                         onComplete();
7100                     }
7101                 }else{
7102                     this.setHeight(oldHeight); // restore original height
7103                     this.setHeight(height, animate, duration, function(){
7104                         this.unclip();
7105                         if(typeof onComplete == "function") onComplete();
7106                     }.createDelegate(this), easing);
7107                 }
7108             }.createDelegate(this), 0);
7109             return this;
7110         },
7111
7112         /**
7113          * Returns true if this element is an ancestor of the passed element
7114          * @param {HTMLElement/String} el The element to check
7115          * @return {Boolean} True if this element is an ancestor of el, else false
7116          */
7117         contains : function(el){
7118             if(!el){return false;}
7119             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7120         },
7121
7122         /**
7123          * Checks whether the element is currently visible using both visibility and display properties.
7124          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7125          * @return {Boolean} True if the element is currently visible, else false
7126          */
7127         isVisible : function(deep) {
7128             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7129             if(deep !== true || !vis){
7130                 return vis;
7131             }
7132             var p = this.dom.parentNode;
7133             while(p && p.tagName.toLowerCase() != "body"){
7134                 if(!Roo.fly(p, '_isVisible').isVisible()){
7135                     return false;
7136                 }
7137                 p = p.parentNode;
7138             }
7139             return true;
7140         },
7141
7142         /**
7143          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7144          * @param {String} selector The CSS selector
7145          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7146          * @return {CompositeElement/CompositeElementLite} The composite element
7147          */
7148         select : function(selector, unique){
7149             return El.select(selector, unique, this.dom);
7150         },
7151
7152         /**
7153          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7154          * @param {String} selector The CSS selector
7155          * @return {Array} An array of the matched nodes
7156          */
7157         query : function(selector, unique){
7158             return Roo.DomQuery.select(selector, this.dom);
7159         },
7160
7161         /**
7162          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7163          * @param {String} selector The CSS selector
7164          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7165          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7166          */
7167         child : function(selector, returnDom){
7168             var n = Roo.DomQuery.selectNode(selector, this.dom);
7169             return returnDom ? n : Roo.get(n);
7170         },
7171
7172         /**
7173          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7174          * @param {String} selector The CSS selector
7175          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7176          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7177          */
7178         down : function(selector, returnDom){
7179             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7180             return returnDom ? n : Roo.get(n);
7181         },
7182
7183         /**
7184          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7185          * @param {String} group The group the DD object is member of
7186          * @param {Object} config The DD config object
7187          * @param {Object} overrides An object containing methods to override/implement on the DD object
7188          * @return {Roo.dd.DD} The DD object
7189          */
7190         initDD : function(group, config, overrides){
7191             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7192             return Roo.apply(dd, overrides);
7193         },
7194
7195         /**
7196          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7197          * @param {String} group The group the DDProxy object is member of
7198          * @param {Object} config The DDProxy config object
7199          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7200          * @return {Roo.dd.DDProxy} The DDProxy object
7201          */
7202         initDDProxy : function(group, config, overrides){
7203             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7204             return Roo.apply(dd, overrides);
7205         },
7206
7207         /**
7208          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7209          * @param {String} group The group the DDTarget object is member of
7210          * @param {Object} config The DDTarget config object
7211          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7212          * @return {Roo.dd.DDTarget} The DDTarget object
7213          */
7214         initDDTarget : function(group, config, overrides){
7215             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7216             return Roo.apply(dd, overrides);
7217         },
7218
7219         /**
7220          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7221          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7222          * @param {Boolean} visible Whether the element is visible
7223          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7224          * @return {Roo.Element} this
7225          */
7226          setVisible : function(visible, animate){
7227             if(!animate || !A){
7228                 if(this.visibilityMode == El.DISPLAY){
7229                     this.setDisplayed(visible);
7230                 }else{
7231                     this.fixDisplay();
7232                     this.dom.style.visibility = visible ? "visible" : "hidden";
7233                 }
7234             }else{
7235                 // closure for composites
7236                 var dom = this.dom;
7237                 var visMode = this.visibilityMode;
7238                 if(visible){
7239                     this.setOpacity(.01);
7240                     this.setVisible(true);
7241                 }
7242                 this.anim({opacity: { to: (visible?1:0) }},
7243                       this.preanim(arguments, 1),
7244                       null, .35, 'easeIn', function(){
7245                          if(!visible){
7246                              if(visMode == El.DISPLAY){
7247                                  dom.style.display = "none";
7248                              }else{
7249                                  dom.style.visibility = "hidden";
7250                              }
7251                              Roo.get(dom).setOpacity(1);
7252                          }
7253                      });
7254             }
7255             return this;
7256         },
7257
7258         /**
7259          * Returns true if display is not "none"
7260          * @return {Boolean}
7261          */
7262         isDisplayed : function() {
7263             return this.getStyle("display") != "none";
7264         },
7265
7266         /**
7267          * Toggles the element's visibility or display, depending on visibility mode.
7268          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7269          * @return {Roo.Element} this
7270          */
7271         toggle : function(animate){
7272             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7273             return this;
7274         },
7275
7276         /**
7277          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7278          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7279          * @return {Roo.Element} this
7280          */
7281         setDisplayed : function(value) {
7282             if(typeof value == "boolean"){
7283                value = value ? this.originalDisplay : "none";
7284             }
7285             this.setStyle("display", value);
7286             return this;
7287         },
7288
7289         /**
7290          * Tries to focus the element. Any exceptions are caught and ignored.
7291          * @return {Roo.Element} this
7292          */
7293         focus : function() {
7294             try{
7295                 this.dom.focus();
7296             }catch(e){}
7297             return this;
7298         },
7299
7300         /**
7301          * Tries to blur the element. Any exceptions are caught and ignored.
7302          * @return {Roo.Element} this
7303          */
7304         blur : function() {
7305             try{
7306                 this.dom.blur();
7307             }catch(e){}
7308             return this;
7309         },
7310
7311         /**
7312          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7313          * @param {String/Array} className The CSS class to add, or an array of classes
7314          * @return {Roo.Element} this
7315          */
7316         addClass : function(className){
7317             if(className instanceof Array){
7318                 for(var i = 0, len = className.length; i < len; i++) {
7319                     this.addClass(className[i]);
7320                 }
7321             }else{
7322                 if(className && !this.hasClass(className)){
7323                     this.dom.className = this.dom.className + " " + className;
7324                 }
7325             }
7326             return this;
7327         },
7328
7329         /**
7330          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7331          * @param {String/Array} className The CSS class to add, or an array of classes
7332          * @return {Roo.Element} this
7333          */
7334         radioClass : function(className){
7335             var siblings = this.dom.parentNode.childNodes;
7336             for(var i = 0; i < siblings.length; i++) {
7337                 var s = siblings[i];
7338                 if(s.nodeType == 1){
7339                     Roo.get(s).removeClass(className);
7340                 }
7341             }
7342             this.addClass(className);
7343             return this;
7344         },
7345
7346         /**
7347          * Removes one or more CSS classes from the element.
7348          * @param {String/Array} className The CSS class to remove, or an array of classes
7349          * @return {Roo.Element} this
7350          */
7351         removeClass : function(className){
7352             if(!className || !this.dom.className){
7353                 return this;
7354             }
7355             if(className instanceof Array){
7356                 for(var i = 0, len = className.length; i < len; i++) {
7357                     this.removeClass(className[i]);
7358                 }
7359             }else{
7360                 if(this.hasClass(className)){
7361                     var re = this.classReCache[className];
7362                     if (!re) {
7363                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7364                        this.classReCache[className] = re;
7365                     }
7366                     this.dom.className =
7367                         this.dom.className.replace(re, " ");
7368                 }
7369             }
7370             return this;
7371         },
7372
7373         // private
7374         classReCache: {},
7375
7376         /**
7377          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7378          * @param {String} className The CSS class to toggle
7379          * @return {Roo.Element} this
7380          */
7381         toggleClass : function(className){
7382             if(this.hasClass(className)){
7383                 this.removeClass(className);
7384             }else{
7385                 this.addClass(className);
7386             }
7387             return this;
7388         },
7389
7390         /**
7391          * Checks if the specified CSS class exists on this element's DOM node.
7392          * @param {String} className The CSS class to check for
7393          * @return {Boolean} True if the class exists, else false
7394          */
7395         hasClass : function(className){
7396             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7397         },
7398
7399         /**
7400          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7401          * @param {String} oldClassName The CSS class to replace
7402          * @param {String} newClassName The replacement CSS class
7403          * @return {Roo.Element} this
7404          */
7405         replaceClass : function(oldClassName, newClassName){
7406             this.removeClass(oldClassName);
7407             this.addClass(newClassName);
7408             return this;
7409         },
7410
7411         /**
7412          * Returns an object with properties matching the styles requested.
7413          * For example, el.getStyles('color', 'font-size', 'width') might return
7414          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7415          * @param {String} style1 A style name
7416          * @param {String} style2 A style name
7417          * @param {String} etc.
7418          * @return {Object} The style object
7419          */
7420         getStyles : function(){
7421             var a = arguments, len = a.length, r = {};
7422             for(var i = 0; i < len; i++){
7423                 r[a[i]] = this.getStyle(a[i]);
7424             }
7425             return r;
7426         },
7427
7428         /**
7429          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7430          * @param {String} property The style property whose value is returned.
7431          * @return {String} The current value of the style property for this element.
7432          */
7433         getStyle : function(){
7434             return view && view.getComputedStyle ?
7435                 function(prop){
7436                     var el = this.dom, v, cs, camel;
7437                     if(prop == 'float'){
7438                         prop = "cssFloat";
7439                     }
7440                     if(el.style && (v = el.style[prop])){
7441                         return v;
7442                     }
7443                     if(cs = view.getComputedStyle(el, "")){
7444                         if(!(camel = propCache[prop])){
7445                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7446                         }
7447                         return cs[camel];
7448                     }
7449                     return null;
7450                 } :
7451                 function(prop){
7452                     var el = this.dom, v, cs, camel;
7453                     if(prop == 'opacity'){
7454                         if(typeof el.style.filter == 'string'){
7455                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7456                             if(m){
7457                                 var fv = parseFloat(m[1]);
7458                                 if(!isNaN(fv)){
7459                                     return fv ? fv / 100 : 0;
7460                                 }
7461                             }
7462                         }
7463                         return 1;
7464                     }else if(prop == 'float'){
7465                         prop = "styleFloat";
7466                     }
7467                     if(!(camel = propCache[prop])){
7468                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7469                     }
7470                     if(v = el.style[camel]){
7471                         return v;
7472                     }
7473                     if(cs = el.currentStyle){
7474                         return cs[camel];
7475                     }
7476                     return null;
7477                 };
7478         }(),
7479
7480         /**
7481          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7482          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7483          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7484          * @return {Roo.Element} this
7485          */
7486         setStyle : function(prop, value){
7487             if(typeof prop == "string"){
7488                 
7489                 if (prop == 'float') {
7490                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7491                     return this;
7492                 }
7493                 
7494                 var camel;
7495                 if(!(camel = propCache[prop])){
7496                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7497                 }
7498                 
7499                 if(camel == 'opacity') {
7500                     this.setOpacity(value);
7501                 }else{
7502                     this.dom.style[camel] = value;
7503                 }
7504             }else{
7505                 for(var style in prop){
7506                     if(typeof prop[style] != "function"){
7507                        this.setStyle(style, prop[style]);
7508                     }
7509                 }
7510             }
7511             return this;
7512         },
7513
7514         /**
7515          * More flexible version of {@link #setStyle} for setting style properties.
7516          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7517          * a function which returns such a specification.
7518          * @return {Roo.Element} this
7519          */
7520         applyStyles : function(style){
7521             Roo.DomHelper.applyStyles(this.dom, style);
7522             return this;
7523         },
7524
7525         /**
7526           * 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).
7527           * @return {Number} The X position of the element
7528           */
7529         getX : function(){
7530             return D.getX(this.dom);
7531         },
7532
7533         /**
7534           * 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).
7535           * @return {Number} The Y position of the element
7536           */
7537         getY : function(){
7538             return D.getY(this.dom);
7539         },
7540
7541         /**
7542           * 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).
7543           * @return {Array} The XY position of the element
7544           */
7545         getXY : function(){
7546             return D.getXY(this.dom);
7547         },
7548
7549         /**
7550          * 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).
7551          * @param {Number} The X position of the element
7552          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7553          * @return {Roo.Element} this
7554          */
7555         setX : function(x, animate){
7556             if(!animate || !A){
7557                 D.setX(this.dom, x);
7558             }else{
7559                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7560             }
7561             return this;
7562         },
7563
7564         /**
7565          * 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).
7566          * @param {Number} The Y position of the element
7567          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7568          * @return {Roo.Element} this
7569          */
7570         setY : function(y, animate){
7571             if(!animate || !A){
7572                 D.setY(this.dom, y);
7573             }else{
7574                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7575             }
7576             return this;
7577         },
7578
7579         /**
7580          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7581          * @param {String} left The left CSS property value
7582          * @return {Roo.Element} this
7583          */
7584         setLeft : function(left){
7585             this.setStyle("left", this.addUnits(left));
7586             return this;
7587         },
7588
7589         /**
7590          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7591          * @param {String} top The top CSS property value
7592          * @return {Roo.Element} this
7593          */
7594         setTop : function(top){
7595             this.setStyle("top", this.addUnits(top));
7596             return this;
7597         },
7598
7599         /**
7600          * Sets the element's CSS right style.
7601          * @param {String} right The right CSS property value
7602          * @return {Roo.Element} this
7603          */
7604         setRight : function(right){
7605             this.setStyle("right", this.addUnits(right));
7606             return this;
7607         },
7608
7609         /**
7610          * Sets the element's CSS bottom style.
7611          * @param {String} bottom The bottom CSS property value
7612          * @return {Roo.Element} this
7613          */
7614         setBottom : function(bottom){
7615             this.setStyle("bottom", this.addUnits(bottom));
7616             return this;
7617         },
7618
7619         /**
7620          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7621          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7622          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7623          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7624          * @return {Roo.Element} this
7625          */
7626         setXY : function(pos, animate){
7627             if(!animate || !A){
7628                 D.setXY(this.dom, pos);
7629             }else{
7630                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7631             }
7632             return this;
7633         },
7634
7635         /**
7636          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7637          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7638          * @param {Number} x X value for new position (coordinates are page-based)
7639          * @param {Number} y Y value for new position (coordinates are page-based)
7640          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7641          * @return {Roo.Element} this
7642          */
7643         setLocation : function(x, y, animate){
7644             this.setXY([x, y], this.preanim(arguments, 2));
7645             return this;
7646         },
7647
7648         /**
7649          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7650          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7651          * @param {Number} x X value for new position (coordinates are page-based)
7652          * @param {Number} y Y value for new position (coordinates are page-based)
7653          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7654          * @return {Roo.Element} this
7655          */
7656         moveTo : function(x, y, animate){
7657             this.setXY([x, y], this.preanim(arguments, 2));
7658             return this;
7659         },
7660
7661         /**
7662          * Returns the region of the given element.
7663          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7664          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7665          */
7666         getRegion : function(){
7667             return D.getRegion(this.dom);
7668         },
7669
7670         /**
7671          * Returns the offset height of the element
7672          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7673          * @return {Number} The element's height
7674          */
7675         getHeight : function(contentHeight){
7676             var h = this.dom.offsetHeight || 0;
7677             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7678         },
7679
7680         /**
7681          * Returns the offset width of the element
7682          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7683          * @return {Number} The element's width
7684          */
7685         getWidth : function(contentWidth){
7686             var w = this.dom.offsetWidth || 0;
7687             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7688         },
7689
7690         /**
7691          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7692          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7693          * if a height has not been set using CSS.
7694          * @return {Number}
7695          */
7696         getComputedHeight : function(){
7697             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7698             if(!h){
7699                 h = parseInt(this.getStyle('height'), 10) || 0;
7700                 if(!this.isBorderBox()){
7701                     h += this.getFrameWidth('tb');
7702                 }
7703             }
7704             return h;
7705         },
7706
7707         /**
7708          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7709          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7710          * if a width has not been set using CSS.
7711          * @return {Number}
7712          */
7713         getComputedWidth : function(){
7714             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7715             if(!w){
7716                 w = parseInt(this.getStyle('width'), 10) || 0;
7717                 if(!this.isBorderBox()){
7718                     w += this.getFrameWidth('lr');
7719                 }
7720             }
7721             return w;
7722         },
7723
7724         /**
7725          * Returns the size of the element.
7726          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7727          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7728          */
7729         getSize : function(contentSize){
7730             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7731         },
7732
7733         /**
7734          * Returns the width and height of the viewport.
7735          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7736          */
7737         getViewSize : function(){
7738             var d = this.dom, doc = document, aw = 0, ah = 0;
7739             if(d == doc || d == doc.body){
7740                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7741             }else{
7742                 return {
7743                     width : d.clientWidth,
7744                     height: d.clientHeight
7745                 };
7746             }
7747         },
7748
7749         /**
7750          * Returns the value of the "value" attribute
7751          * @param {Boolean} asNumber true to parse the value as a number
7752          * @return {String/Number}
7753          */
7754         getValue : function(asNumber){
7755             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7756         },
7757
7758         // private
7759         adjustWidth : function(width){
7760             if(typeof width == "number"){
7761                 if(this.autoBoxAdjust && !this.isBorderBox()){
7762                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7763                 }
7764                 if(width < 0){
7765                     width = 0;
7766                 }
7767             }
7768             return width;
7769         },
7770
7771         // private
7772         adjustHeight : function(height){
7773             if(typeof height == "number"){
7774                if(this.autoBoxAdjust && !this.isBorderBox()){
7775                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7776                }
7777                if(height < 0){
7778                    height = 0;
7779                }
7780             }
7781             return height;
7782         },
7783
7784         /**
7785          * Set the width of the element
7786          * @param {Number} width The new width
7787          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7788          * @return {Roo.Element} this
7789          */
7790         setWidth : function(width, animate){
7791             width = this.adjustWidth(width);
7792             if(!animate || !A){
7793                 this.dom.style.width = this.addUnits(width);
7794             }else{
7795                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7796             }
7797             return this;
7798         },
7799
7800         /**
7801          * Set the height of the element
7802          * @param {Number} height The new height
7803          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7804          * @return {Roo.Element} this
7805          */
7806          setHeight : function(height, animate){
7807             height = this.adjustHeight(height);
7808             if(!animate || !A){
7809                 this.dom.style.height = this.addUnits(height);
7810             }else{
7811                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7812             }
7813             return this;
7814         },
7815
7816         /**
7817          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7818          * @param {Number} width The new width
7819          * @param {Number} height The new height
7820          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7821          * @return {Roo.Element} this
7822          */
7823          setSize : function(width, height, animate){
7824             if(typeof width == "object"){ // in case of object from getSize()
7825                 height = width.height; width = width.width;
7826             }
7827             width = this.adjustWidth(width); height = this.adjustHeight(height);
7828             if(!animate || !A){
7829                 this.dom.style.width = this.addUnits(width);
7830                 this.dom.style.height = this.addUnits(height);
7831             }else{
7832                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7833             }
7834             return this;
7835         },
7836
7837         /**
7838          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7839          * @param {Number} x X value for new position (coordinates are page-based)
7840          * @param {Number} y Y value for new position (coordinates are page-based)
7841          * @param {Number} width The new width
7842          * @param {Number} height The new height
7843          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7844          * @return {Roo.Element} this
7845          */
7846         setBounds : function(x, y, width, height, animate){
7847             if(!animate || !A){
7848                 this.setSize(width, height);
7849                 this.setLocation(x, y);
7850             }else{
7851                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7852                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7853                               this.preanim(arguments, 4), 'motion');
7854             }
7855             return this;
7856         },
7857
7858         /**
7859          * 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.
7860          * @param {Roo.lib.Region} region The region to fill
7861          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7862          * @return {Roo.Element} this
7863          */
7864         setRegion : function(region, animate){
7865             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7866             return this;
7867         },
7868
7869         /**
7870          * Appends an event handler
7871          *
7872          * @param {String}   eventName     The type of event to append
7873          * @param {Function} fn        The method the event invokes
7874          * @param {Object} scope       (optional) The scope (this object) of the fn
7875          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7876          */
7877         addListener : function(eventName, fn, scope, options){
7878             if (this.dom) {
7879                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7880             }
7881         },
7882
7883         /**
7884          * Removes an event handler from this element
7885          * @param {String} eventName the type of event to remove
7886          * @param {Function} fn the method the event invokes
7887          * @return {Roo.Element} this
7888          */
7889         removeListener : function(eventName, fn){
7890             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7891             return this;
7892         },
7893
7894         /**
7895          * Removes all previous added listeners from this element
7896          * @return {Roo.Element} this
7897          */
7898         removeAllListeners : function(){
7899             E.purgeElement(this.dom);
7900             return this;
7901         },
7902
7903         relayEvent : function(eventName, observable){
7904             this.on(eventName, function(e){
7905                 observable.fireEvent(eventName, e);
7906             });
7907         },
7908
7909         /**
7910          * Set the opacity of the element
7911          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7912          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7913          * @return {Roo.Element} this
7914          */
7915          setOpacity : function(opacity, animate){
7916             if(!animate || !A){
7917                 var s = this.dom.style;
7918                 if(Roo.isIE){
7919                     s.zoom = 1;
7920                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7921                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7922                 }else{
7923                     s.opacity = opacity;
7924                 }
7925             }else{
7926                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7927             }
7928             return this;
7929         },
7930
7931         /**
7932          * Gets the left X coordinate
7933          * @param {Boolean} local True to get the local css position instead of page coordinate
7934          * @return {Number}
7935          */
7936         getLeft : function(local){
7937             if(!local){
7938                 return this.getX();
7939             }else{
7940                 return parseInt(this.getStyle("left"), 10) || 0;
7941             }
7942         },
7943
7944         /**
7945          * Gets the right X coordinate of the element (element X position + element width)
7946          * @param {Boolean} local True to get the local css position instead of page coordinate
7947          * @return {Number}
7948          */
7949         getRight : function(local){
7950             if(!local){
7951                 return this.getX() + this.getWidth();
7952             }else{
7953                 return (this.getLeft(true) + this.getWidth()) || 0;
7954             }
7955         },
7956
7957         /**
7958          * Gets the top Y coordinate
7959          * @param {Boolean} local True to get the local css position instead of page coordinate
7960          * @return {Number}
7961          */
7962         getTop : function(local) {
7963             if(!local){
7964                 return this.getY();
7965             }else{
7966                 return parseInt(this.getStyle("top"), 10) || 0;
7967             }
7968         },
7969
7970         /**
7971          * Gets the bottom Y coordinate of the element (element Y position + element height)
7972          * @param {Boolean} local True to get the local css position instead of page coordinate
7973          * @return {Number}
7974          */
7975         getBottom : function(local){
7976             if(!local){
7977                 return this.getY() + this.getHeight();
7978             }else{
7979                 return (this.getTop(true) + this.getHeight()) || 0;
7980             }
7981         },
7982
7983         /**
7984         * Initializes positioning on this element. If a desired position is not passed, it will make the
7985         * the element positioned relative IF it is not already positioned.
7986         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7987         * @param {Number} zIndex (optional) The zIndex to apply
7988         * @param {Number} x (optional) Set the page X position
7989         * @param {Number} y (optional) Set the page Y position
7990         */
7991         position : function(pos, zIndex, x, y){
7992             if(!pos){
7993                if(this.getStyle('position') == 'static'){
7994                    this.setStyle('position', 'relative');
7995                }
7996             }else{
7997                 this.setStyle("position", pos);
7998             }
7999             if(zIndex){
8000                 this.setStyle("z-index", zIndex);
8001             }
8002             if(x !== undefined && y !== undefined){
8003                 this.setXY([x, y]);
8004             }else if(x !== undefined){
8005                 this.setX(x);
8006             }else if(y !== undefined){
8007                 this.setY(y);
8008             }
8009         },
8010
8011         /**
8012         * Clear positioning back to the default when the document was loaded
8013         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8014         * @return {Roo.Element} this
8015          */
8016         clearPositioning : function(value){
8017             value = value ||'';
8018             this.setStyle({
8019                 "left": value,
8020                 "right": value,
8021                 "top": value,
8022                 "bottom": value,
8023                 "z-index": "",
8024                 "position" : "static"
8025             });
8026             return this;
8027         },
8028
8029         /**
8030         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8031         * snapshot before performing an update and then restoring the element.
8032         * @return {Object}
8033         */
8034         getPositioning : function(){
8035             var l = this.getStyle("left");
8036             var t = this.getStyle("top");
8037             return {
8038                 "position" : this.getStyle("position"),
8039                 "left" : l,
8040                 "right" : l ? "" : this.getStyle("right"),
8041                 "top" : t,
8042                 "bottom" : t ? "" : this.getStyle("bottom"),
8043                 "z-index" : this.getStyle("z-index")
8044             };
8045         },
8046
8047         /**
8048          * Gets the width of the border(s) for the specified side(s)
8049          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8050          * passing lr would get the border (l)eft width + the border (r)ight width.
8051          * @return {Number} The width of the sides passed added together
8052          */
8053         getBorderWidth : function(side){
8054             return this.addStyles(side, El.borders);
8055         },
8056
8057         /**
8058          * Gets the width of the padding(s) for the specified side(s)
8059          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8060          * passing lr would get the padding (l)eft + the padding (r)ight.
8061          * @return {Number} The padding of the sides passed added together
8062          */
8063         getPadding : function(side){
8064             return this.addStyles(side, El.paddings);
8065         },
8066
8067         /**
8068         * Set positioning with an object returned by getPositioning().
8069         * @param {Object} posCfg
8070         * @return {Roo.Element} this
8071          */
8072         setPositioning : function(pc){
8073             this.applyStyles(pc);
8074             if(pc.right == "auto"){
8075                 this.dom.style.right = "";
8076             }
8077             if(pc.bottom == "auto"){
8078                 this.dom.style.bottom = "";
8079             }
8080             return this;
8081         },
8082
8083         // private
8084         fixDisplay : function(){
8085             if(this.getStyle("display") == "none"){
8086                 this.setStyle("visibility", "hidden");
8087                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8088                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8089                     this.setStyle("display", "block");
8090                 }
8091             }
8092         },
8093
8094         /**
8095          * Quick set left and top adding default units
8096          * @param {String} left The left CSS property value
8097          * @param {String} top The top CSS property value
8098          * @return {Roo.Element} this
8099          */
8100          setLeftTop : function(left, top){
8101             this.dom.style.left = this.addUnits(left);
8102             this.dom.style.top = this.addUnits(top);
8103             return this;
8104         },
8105
8106         /**
8107          * Move this element relative to its current position.
8108          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8109          * @param {Number} distance How far to move the element in pixels
8110          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8111          * @return {Roo.Element} this
8112          */
8113          move : function(direction, distance, animate){
8114             var xy = this.getXY();
8115             direction = direction.toLowerCase();
8116             switch(direction){
8117                 case "l":
8118                 case "left":
8119                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8120                     break;
8121                case "r":
8122                case "right":
8123                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8124                     break;
8125                case "t":
8126                case "top":
8127                case "up":
8128                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8129                     break;
8130                case "b":
8131                case "bottom":
8132                case "down":
8133                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8134                     break;
8135             }
8136             return this;
8137         },
8138
8139         /**
8140          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8141          * @return {Roo.Element} this
8142          */
8143         clip : function(){
8144             if(!this.isClipped){
8145                this.isClipped = true;
8146                this.originalClip = {
8147                    "o": this.getStyle("overflow"),
8148                    "x": this.getStyle("overflow-x"),
8149                    "y": this.getStyle("overflow-y")
8150                };
8151                this.setStyle("overflow", "hidden");
8152                this.setStyle("overflow-x", "hidden");
8153                this.setStyle("overflow-y", "hidden");
8154             }
8155             return this;
8156         },
8157
8158         /**
8159          *  Return clipping (overflow) to original clipping before clip() was called
8160          * @return {Roo.Element} this
8161          */
8162         unclip : function(){
8163             if(this.isClipped){
8164                 this.isClipped = false;
8165                 var o = this.originalClip;
8166                 if(o.o){this.setStyle("overflow", o.o);}
8167                 if(o.x){this.setStyle("overflow-x", o.x);}
8168                 if(o.y){this.setStyle("overflow-y", o.y);}
8169             }
8170             return this;
8171         },
8172
8173
8174         /**
8175          * Gets the x,y coordinates specified by the anchor position on the element.
8176          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8177          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8178          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8179          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8180          * @return {Array} [x, y] An array containing the element's x and y coordinates
8181          */
8182         getAnchorXY : function(anchor, local, s){
8183             //Passing a different size is useful for pre-calculating anchors,
8184             //especially for anchored animations that change the el size.
8185
8186             var w, h, vp = false;
8187             if(!s){
8188                 var d = this.dom;
8189                 if(d == document.body || d == document){
8190                     vp = true;
8191                     w = D.getViewWidth(); h = D.getViewHeight();
8192                 }else{
8193                     w = this.getWidth(); h = this.getHeight();
8194                 }
8195             }else{
8196                 w = s.width;  h = s.height;
8197             }
8198             var x = 0, y = 0, r = Math.round;
8199             switch((anchor || "tl").toLowerCase()){
8200                 case "c":
8201                     x = r(w*.5);
8202                     y = r(h*.5);
8203                 break;
8204                 case "t":
8205                     x = r(w*.5);
8206                     y = 0;
8207                 break;
8208                 case "l":
8209                     x = 0;
8210                     y = r(h*.5);
8211                 break;
8212                 case "r":
8213                     x = w;
8214                     y = r(h*.5);
8215                 break;
8216                 case "b":
8217                     x = r(w*.5);
8218                     y = h;
8219                 break;
8220                 case "tl":
8221                     x = 0;
8222                     y = 0;
8223                 break;
8224                 case "bl":
8225                     x = 0;
8226                     y = h;
8227                 break;
8228                 case "br":
8229                     x = w;
8230                     y = h;
8231                 break;
8232                 case "tr":
8233                     x = w;
8234                     y = 0;
8235                 break;
8236             }
8237             if(local === true){
8238                 return [x, y];
8239             }
8240             if(vp){
8241                 var sc = this.getScroll();
8242                 return [x + sc.left, y + sc.top];
8243             }
8244             //Add the element's offset xy
8245             var o = this.getXY();
8246             return [x+o[0], y+o[1]];
8247         },
8248
8249         /**
8250          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8251          * supported position values.
8252          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8253          * @param {String} position The position to align to.
8254          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8255          * @return {Array} [x, y]
8256          */
8257         getAlignToXY : function(el, p, o){
8258             el = Roo.get(el);
8259             var d = this.dom;
8260             if(!el.dom){
8261                 throw "Element.alignTo with an element that doesn't exist";
8262             }
8263             var c = false; //constrain to viewport
8264             var p1 = "", p2 = "";
8265             o = o || [0,0];
8266
8267             if(!p){
8268                 p = "tl-bl";
8269             }else if(p == "?"){
8270                 p = "tl-bl?";
8271             }else if(p.indexOf("-") == -1){
8272                 p = "tl-" + p;
8273             }
8274             p = p.toLowerCase();
8275             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8276             if(!m){
8277                throw "Element.alignTo with an invalid alignment " + p;
8278             }
8279             p1 = m[1]; p2 = m[2]; c = !!m[3];
8280
8281             //Subtract the aligned el's internal xy from the target's offset xy
8282             //plus custom offset to get the aligned el's new offset xy
8283             var a1 = this.getAnchorXY(p1, true);
8284             var a2 = el.getAnchorXY(p2, false);
8285             var x = a2[0] - a1[0] + o[0];
8286             var y = a2[1] - a1[1] + o[1];
8287             if(c){
8288                 //constrain the aligned el to viewport if necessary
8289                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8290                 // 5px of margin for ie
8291                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8292
8293                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8294                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8295                 //otherwise swap the aligned el to the opposite border of the target.
8296                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8297                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8298                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8299                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8300
8301                var doc = document;
8302                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8303                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8304
8305                if((x+w) > dw + scrollX){
8306                     x = swapX ? r.left-w : dw+scrollX-w;
8307                 }
8308                if(x < scrollX){
8309                    x = swapX ? r.right : scrollX;
8310                }
8311                if((y+h) > dh + scrollY){
8312                     y = swapY ? r.top-h : dh+scrollY-h;
8313                 }
8314                if (y < scrollY){
8315                    y = swapY ? r.bottom : scrollY;
8316                }
8317             }
8318             return [x,y];
8319         },
8320
8321         // private
8322         getConstrainToXY : function(){
8323             var os = {top:0, left:0, bottom:0, right: 0};
8324
8325             return function(el, local, offsets, proposedXY){
8326                 el = Roo.get(el);
8327                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8328
8329                 var vw, vh, vx = 0, vy = 0;
8330                 if(el.dom == document.body || el.dom == document){
8331                     vw = Roo.lib.Dom.getViewWidth();
8332                     vh = Roo.lib.Dom.getViewHeight();
8333                 }else{
8334                     vw = el.dom.clientWidth;
8335                     vh = el.dom.clientHeight;
8336                     if(!local){
8337                         var vxy = el.getXY();
8338                         vx = vxy[0];
8339                         vy = vxy[1];
8340                     }
8341                 }
8342
8343                 var s = el.getScroll();
8344
8345                 vx += offsets.left + s.left;
8346                 vy += offsets.top + s.top;
8347
8348                 vw -= offsets.right;
8349                 vh -= offsets.bottom;
8350
8351                 var vr = vx+vw;
8352                 var vb = vy+vh;
8353
8354                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8355                 var x = xy[0], y = xy[1];
8356                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8357
8358                 // only move it if it needs it
8359                 var moved = false;
8360
8361                 // first validate right/bottom
8362                 if((x + w) > vr){
8363                     x = vr - w;
8364                     moved = true;
8365                 }
8366                 if((y + h) > vb){
8367                     y = vb - h;
8368                     moved = true;
8369                 }
8370                 // then make sure top/left isn't negative
8371                 if(x < vx){
8372                     x = vx;
8373                     moved = true;
8374                 }
8375                 if(y < vy){
8376                     y = vy;
8377                     moved = true;
8378                 }
8379                 return moved ? [x, y] : false;
8380             };
8381         }(),
8382
8383         // private
8384         adjustForConstraints : function(xy, parent, offsets){
8385             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8386         },
8387
8388         /**
8389          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8390          * document it aligns it to the viewport.
8391          * The position parameter is optional, and can be specified in any one of the following formats:
8392          * <ul>
8393          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8394          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8395          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8396          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8397          *   <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
8398          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8399          * </ul>
8400          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8401          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8402          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8403          * that specified in order to enforce the viewport constraints.
8404          * Following are all of the supported anchor positions:
8405     <pre>
8406     Value  Description
8407     -----  -----------------------------
8408     tl     The top left corner (default)
8409     t      The center of the top edge
8410     tr     The top right corner
8411     l      The center of the left edge
8412     c      In the center of the element
8413     r      The center of the right edge
8414     bl     The bottom left corner
8415     b      The center of the bottom edge
8416     br     The bottom right corner
8417     </pre>
8418     Example Usage:
8419     <pre><code>
8420     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8421     el.alignTo("other-el");
8422
8423     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8424     el.alignTo("other-el", "tr?");
8425
8426     // align the bottom right corner of el with the center left edge of other-el
8427     el.alignTo("other-el", "br-l?");
8428
8429     // align the center of el with the bottom left corner of other-el and
8430     // adjust the x position by -6 pixels (and the y position by 0)
8431     el.alignTo("other-el", "c-bl", [-6, 0]);
8432     </code></pre>
8433          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8434          * @param {String} position The position to align to.
8435          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8436          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8437          * @return {Roo.Element} this
8438          */
8439         alignTo : function(element, position, offsets, animate){
8440             var xy = this.getAlignToXY(element, position, offsets);
8441             this.setXY(xy, this.preanim(arguments, 3));
8442             return this;
8443         },
8444
8445         /**
8446          * Anchors an element to another element and realigns it when the window is resized.
8447          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8448          * @param {String} position The position to align to.
8449          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8450          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8451          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8452          * is a number, it is used as the buffer delay (defaults to 50ms).
8453          * @param {Function} callback The function to call after the animation finishes
8454          * @return {Roo.Element} this
8455          */
8456         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8457             var action = function(){
8458                 this.alignTo(el, alignment, offsets, animate);
8459                 Roo.callback(callback, this);
8460             };
8461             Roo.EventManager.onWindowResize(action, this);
8462             var tm = typeof monitorScroll;
8463             if(tm != 'undefined'){
8464                 Roo.EventManager.on(window, 'scroll', action, this,
8465                     {buffer: tm == 'number' ? monitorScroll : 50});
8466             }
8467             action.call(this); // align immediately
8468             return this;
8469         },
8470         /**
8471          * Clears any opacity settings from this element. Required in some cases for IE.
8472          * @return {Roo.Element} this
8473          */
8474         clearOpacity : function(){
8475             if (window.ActiveXObject) {
8476                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8477                     this.dom.style.filter = "";
8478                 }
8479             } else {
8480                 this.dom.style.opacity = "";
8481                 this.dom.style["-moz-opacity"] = "";
8482                 this.dom.style["-khtml-opacity"] = "";
8483             }
8484             return this;
8485         },
8486
8487         /**
8488          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8489          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8490          * @return {Roo.Element} this
8491          */
8492         hide : function(animate){
8493             this.setVisible(false, this.preanim(arguments, 0));
8494             return this;
8495         },
8496
8497         /**
8498         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8499         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8500          * @return {Roo.Element} this
8501          */
8502         show : function(animate){
8503             this.setVisible(true, this.preanim(arguments, 0));
8504             return this;
8505         },
8506
8507         /**
8508          * @private Test if size has a unit, otherwise appends the default
8509          */
8510         addUnits : function(size){
8511             return Roo.Element.addUnits(size, this.defaultUnit);
8512         },
8513
8514         /**
8515          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8516          * @return {Roo.Element} this
8517          */
8518         beginMeasure : function(){
8519             var el = this.dom;
8520             if(el.offsetWidth || el.offsetHeight){
8521                 return this; // offsets work already
8522             }
8523             var changed = [];
8524             var p = this.dom, b = document.body; // start with this element
8525             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8526                 var pe = Roo.get(p);
8527                 if(pe.getStyle('display') == 'none'){
8528                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8529                     p.style.visibility = "hidden";
8530                     p.style.display = "block";
8531                 }
8532                 p = p.parentNode;
8533             }
8534             this._measureChanged = changed;
8535             return this;
8536
8537         },
8538
8539         /**
8540          * Restores displays to before beginMeasure was called
8541          * @return {Roo.Element} this
8542          */
8543         endMeasure : function(){
8544             var changed = this._measureChanged;
8545             if(changed){
8546                 for(var i = 0, len = changed.length; i < len; i++) {
8547                     var r = changed[i];
8548                     r.el.style.visibility = r.visibility;
8549                     r.el.style.display = "none";
8550                 }
8551                 this._measureChanged = null;
8552             }
8553             return this;
8554         },
8555
8556         /**
8557         * Update the innerHTML of this element, optionally searching for and processing scripts
8558         * @param {String} html The new HTML
8559         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8560         * @param {Function} callback For async script loading you can be noticed when the update completes
8561         * @return {Roo.Element} this
8562          */
8563         update : function(html, loadScripts, callback){
8564             if(typeof html == "undefined"){
8565                 html = "";
8566             }
8567             if(loadScripts !== true){
8568                 this.dom.innerHTML = html;
8569                 if(typeof callback == "function"){
8570                     callback();
8571                 }
8572                 return this;
8573             }
8574             var id = Roo.id();
8575             var dom = this.dom;
8576
8577             html += '<span id="' + id + '"></span>';
8578
8579             E.onAvailable(id, function(){
8580                 var hd = document.getElementsByTagName("head")[0];
8581                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8582                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8583                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8584
8585                 var match;
8586                 while(match = re.exec(html)){
8587                     var attrs = match[1];
8588                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8589                     if(srcMatch && srcMatch[2]){
8590                        var s = document.createElement("script");
8591                        s.src = srcMatch[2];
8592                        var typeMatch = attrs.match(typeRe);
8593                        if(typeMatch && typeMatch[2]){
8594                            s.type = typeMatch[2];
8595                        }
8596                        hd.appendChild(s);
8597                     }else if(match[2] && match[2].length > 0){
8598                         if(window.execScript) {
8599                            window.execScript(match[2]);
8600                         } else {
8601                             /**
8602                              * eval:var:id
8603                              * eval:var:dom
8604                              * eval:var:html
8605                              * 
8606                              */
8607                            window.eval(match[2]);
8608                         }
8609                     }
8610                 }
8611                 var el = document.getElementById(id);
8612                 if(el){el.parentNode.removeChild(el);}
8613                 if(typeof callback == "function"){
8614                     callback();
8615                 }
8616             });
8617             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8618             return this;
8619         },
8620
8621         /**
8622          * Direct access to the UpdateManager update() method (takes the same parameters).
8623          * @param {String/Function} url The url for this request or a function to call to get the url
8624          * @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}
8625          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8626          * @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.
8627          * @return {Roo.Element} this
8628          */
8629         load : function(){
8630             var um = this.getUpdateManager();
8631             um.update.apply(um, arguments);
8632             return this;
8633         },
8634
8635         /**
8636         * Gets this element's UpdateManager
8637         * @return {Roo.UpdateManager} The UpdateManager
8638         */
8639         getUpdateManager : function(){
8640             if(!this.updateManager){
8641                 this.updateManager = new Roo.UpdateManager(this);
8642             }
8643             return this.updateManager;
8644         },
8645
8646         /**
8647          * Disables text selection for this element (normalized across browsers)
8648          * @return {Roo.Element} this
8649          */
8650         unselectable : function(){
8651             this.dom.unselectable = "on";
8652             this.swallowEvent("selectstart", true);
8653             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8654             this.addClass("x-unselectable");
8655             return this;
8656         },
8657
8658         /**
8659         * Calculates the x, y to center this element on the screen
8660         * @return {Array} The x, y values [x, y]
8661         */
8662         getCenterXY : function(){
8663             return this.getAlignToXY(document, 'c-c');
8664         },
8665
8666         /**
8667         * Centers the Element in either the viewport, or another Element.
8668         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8669         */
8670         center : function(centerIn){
8671             this.alignTo(centerIn || document, 'c-c');
8672             return this;
8673         },
8674
8675         /**
8676          * Tests various css rules/browsers to determine if this element uses a border box
8677          * @return {Boolean}
8678          */
8679         isBorderBox : function(){
8680             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8681         },
8682
8683         /**
8684          * Return a box {x, y, width, height} that can be used to set another elements
8685          * size/location to match this element.
8686          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8687          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8688          * @return {Object} box An object in the format {x, y, width, height}
8689          */
8690         getBox : function(contentBox, local){
8691             var xy;
8692             if(!local){
8693                 xy = this.getXY();
8694             }else{
8695                 var left = parseInt(this.getStyle("left"), 10) || 0;
8696                 var top = parseInt(this.getStyle("top"), 10) || 0;
8697                 xy = [left, top];
8698             }
8699             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8700             if(!contentBox){
8701                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8702             }else{
8703                 var l = this.getBorderWidth("l")+this.getPadding("l");
8704                 var r = this.getBorderWidth("r")+this.getPadding("r");
8705                 var t = this.getBorderWidth("t")+this.getPadding("t");
8706                 var b = this.getBorderWidth("b")+this.getPadding("b");
8707                 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)};
8708             }
8709             bx.right = bx.x + bx.width;
8710             bx.bottom = bx.y + bx.height;
8711             return bx;
8712         },
8713
8714         /**
8715          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8716          for more information about the sides.
8717          * @param {String} sides
8718          * @return {Number}
8719          */
8720         getFrameWidth : function(sides, onlyContentBox){
8721             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8722         },
8723
8724         /**
8725          * 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.
8726          * @param {Object} box The box to fill {x, y, width, height}
8727          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8728          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8729          * @return {Roo.Element} this
8730          */
8731         setBox : function(box, adjust, animate){
8732             var w = box.width, h = box.height;
8733             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8734                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8735                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8736             }
8737             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8738             return this;
8739         },
8740
8741         /**
8742          * Forces the browser to repaint this element
8743          * @return {Roo.Element} this
8744          */
8745          repaint : function(){
8746             var dom = this.dom;
8747             this.addClass("x-repaint");
8748             setTimeout(function(){
8749                 Roo.get(dom).removeClass("x-repaint");
8750             }, 1);
8751             return this;
8752         },
8753
8754         /**
8755          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8756          * then it returns the calculated width of the sides (see getPadding)
8757          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8758          * @return {Object/Number}
8759          */
8760         getMargins : function(side){
8761             if(!side){
8762                 return {
8763                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8764                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8765                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8766                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8767                 };
8768             }else{
8769                 return this.addStyles(side, El.margins);
8770              }
8771         },
8772
8773         // private
8774         addStyles : function(sides, styles){
8775             var val = 0, v, w;
8776             for(var i = 0, len = sides.length; i < len; i++){
8777                 v = this.getStyle(styles[sides.charAt(i)]);
8778                 if(v){
8779                      w = parseInt(v, 10);
8780                      if(w){ val += w; }
8781                 }
8782             }
8783             return val;
8784         },
8785
8786         /**
8787          * Creates a proxy element of this element
8788          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8789          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8790          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8791          * @return {Roo.Element} The new proxy element
8792          */
8793         createProxy : function(config, renderTo, matchBox){
8794             if(renderTo){
8795                 renderTo = Roo.getDom(renderTo);
8796             }else{
8797                 renderTo = document.body;
8798             }
8799             config = typeof config == "object" ?
8800                 config : {tag : "div", cls: config};
8801             var proxy = Roo.DomHelper.append(renderTo, config, true);
8802             if(matchBox){
8803                proxy.setBox(this.getBox());
8804             }
8805             return proxy;
8806         },
8807
8808         /**
8809          * Puts a mask over this element to disable user interaction. Requires core.css.
8810          * This method can only be applied to elements which accept child nodes.
8811          * @param {String} msg (optional) A message to display in the mask
8812          * @param {String} msgCls (optional) A css class to apply to the msg element
8813          * @return {Element} The mask  element
8814          */
8815         mask : function(msg, msgCls){
8816             if(this.getStyle("position") == "static"){
8817                 this.setStyle("position", "relative");
8818             }
8819             if(!this._mask){
8820                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8821             }
8822             this.addClass("x-masked");
8823             this._mask.setDisplayed(true);
8824             if(typeof msg == 'string'){
8825                 if(!this._maskMsg){
8826                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8827                 }
8828                 var mm = this._maskMsg;
8829                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8830                 mm.dom.firstChild.innerHTML = msg;
8831                 mm.setDisplayed(true);
8832                 mm.center(this);
8833             }
8834             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8835                 this._mask.setHeight(this.getHeight());
8836             }
8837             return this._mask;
8838         },
8839
8840         /**
8841          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8842          * it is cached for reuse.
8843          */
8844         unmask : function(removeEl){
8845             if(this._mask){
8846                 if(removeEl === true){
8847                     this._mask.remove();
8848                     delete this._mask;
8849                     if(this._maskMsg){
8850                         this._maskMsg.remove();
8851                         delete this._maskMsg;
8852                     }
8853                 }else{
8854                     this._mask.setDisplayed(false);
8855                     if(this._maskMsg){
8856                         this._maskMsg.setDisplayed(false);
8857                     }
8858                 }
8859             }
8860             this.removeClass("x-masked");
8861         },
8862
8863         /**
8864          * Returns true if this element is masked
8865          * @return {Boolean}
8866          */
8867         isMasked : function(){
8868             return this._mask && this._mask.isVisible();
8869         },
8870
8871         /**
8872          * Creates an iframe shim for this element to keep selects and other windowed objects from
8873          * showing through.
8874          * @return {Roo.Element} The new shim element
8875          */
8876         createShim : function(){
8877             var el = document.createElement('iframe');
8878             el.frameBorder = 'no';
8879             el.className = 'roo-shim';
8880             if(Roo.isIE && Roo.isSecure){
8881                 el.src = Roo.SSL_SECURE_URL;
8882             }
8883             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8884             shim.autoBoxAdjust = false;
8885             return shim;
8886         },
8887
8888         /**
8889          * Removes this element from the DOM and deletes it from the cache
8890          */
8891         remove : function(){
8892             if(this.dom.parentNode){
8893                 this.dom.parentNode.removeChild(this.dom);
8894             }
8895             delete El.cache[this.dom.id];
8896         },
8897
8898         /**
8899          * Sets up event handlers to add and remove a css class when the mouse is over this element
8900          * @param {String} className
8901          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8902          * mouseout events for children elements
8903          * @return {Roo.Element} this
8904          */
8905         addClassOnOver : function(className, preventFlicker){
8906             this.on("mouseover", function(){
8907                 Roo.fly(this, '_internal').addClass(className);
8908             }, this.dom);
8909             var removeFn = function(e){
8910                 if(preventFlicker !== true || !e.within(this, true)){
8911                     Roo.fly(this, '_internal').removeClass(className);
8912                 }
8913             };
8914             this.on("mouseout", removeFn, this.dom);
8915             return this;
8916         },
8917
8918         /**
8919          * Sets up event handlers to add and remove a css class when this element has the focus
8920          * @param {String} className
8921          * @return {Roo.Element} this
8922          */
8923         addClassOnFocus : function(className){
8924             this.on("focus", function(){
8925                 Roo.fly(this, '_internal').addClass(className);
8926             }, this.dom);
8927             this.on("blur", function(){
8928                 Roo.fly(this, '_internal').removeClass(className);
8929             }, this.dom);
8930             return this;
8931         },
8932         /**
8933          * 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)
8934          * @param {String} className
8935          * @return {Roo.Element} this
8936          */
8937         addClassOnClick : function(className){
8938             var dom = this.dom;
8939             this.on("mousedown", function(){
8940                 Roo.fly(dom, '_internal').addClass(className);
8941                 var d = Roo.get(document);
8942                 var fn = function(){
8943                     Roo.fly(dom, '_internal').removeClass(className);
8944                     d.removeListener("mouseup", fn);
8945                 };
8946                 d.on("mouseup", fn);
8947             });
8948             return this;
8949         },
8950
8951         /**
8952          * Stops the specified event from bubbling and optionally prevents the default action
8953          * @param {String} eventName
8954          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8955          * @return {Roo.Element} this
8956          */
8957         swallowEvent : function(eventName, preventDefault){
8958             var fn = function(e){
8959                 e.stopPropagation();
8960                 if(preventDefault){
8961                     e.preventDefault();
8962                 }
8963             };
8964             if(eventName instanceof Array){
8965                 for(var i = 0, len = eventName.length; i < len; i++){
8966                      this.on(eventName[i], fn);
8967                 }
8968                 return this;
8969             }
8970             this.on(eventName, fn);
8971             return this;
8972         },
8973
8974         /**
8975          * @private
8976          */
8977       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8978
8979         /**
8980          * Sizes this element to its parent element's dimensions performing
8981          * neccessary box adjustments.
8982          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8983          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8984          * @return {Roo.Element} this
8985          */
8986         fitToParent : function(monitorResize, targetParent) {
8987           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8988           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8989           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8990             return;
8991           }
8992           var p = Roo.get(targetParent || this.dom.parentNode);
8993           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8994           if (monitorResize === true) {
8995             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8996             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8997           }
8998           return this;
8999         },
9000
9001         /**
9002          * Gets the next sibling, skipping text nodes
9003          * @return {HTMLElement} The next sibling or null
9004          */
9005         getNextSibling : function(){
9006             var n = this.dom.nextSibling;
9007             while(n && n.nodeType != 1){
9008                 n = n.nextSibling;
9009             }
9010             return n;
9011         },
9012
9013         /**
9014          * Gets the previous sibling, skipping text nodes
9015          * @return {HTMLElement} The previous sibling or null
9016          */
9017         getPrevSibling : function(){
9018             var n = this.dom.previousSibling;
9019             while(n && n.nodeType != 1){
9020                 n = n.previousSibling;
9021             }
9022             return n;
9023         },
9024
9025
9026         /**
9027          * Appends the passed element(s) to this element
9028          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9029          * @return {Roo.Element} this
9030          */
9031         appendChild: function(el){
9032             el = Roo.get(el);
9033             el.appendTo(this);
9034             return this;
9035         },
9036
9037         /**
9038          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9039          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9040          * automatically generated with the specified attributes.
9041          * @param {HTMLElement} insertBefore (optional) a child element of this element
9042          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9043          * @return {Roo.Element} The new child element
9044          */
9045         createChild: function(config, insertBefore, returnDom){
9046             config = config || {tag:'div'};
9047             if(insertBefore){
9048                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9049             }
9050             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9051         },
9052
9053         /**
9054          * Appends this element to the passed element
9055          * @param {String/HTMLElement/Element} el The new parent element
9056          * @return {Roo.Element} this
9057          */
9058         appendTo: function(el){
9059             el = Roo.getDom(el);
9060             el.appendChild(this.dom);
9061             return this;
9062         },
9063
9064         /**
9065          * Inserts this element before the passed element in the DOM
9066          * @param {String/HTMLElement/Element} el The element to insert before
9067          * @return {Roo.Element} this
9068          */
9069         insertBefore: function(el){
9070             el = Roo.getDom(el);
9071             el.parentNode.insertBefore(this.dom, el);
9072             return this;
9073         },
9074
9075         /**
9076          * Inserts this element after the passed element in the DOM
9077          * @param {String/HTMLElement/Element} el The element to insert after
9078          * @return {Roo.Element} this
9079          */
9080         insertAfter: function(el){
9081             el = Roo.getDom(el);
9082             el.parentNode.insertBefore(this.dom, el.nextSibling);
9083             return this;
9084         },
9085
9086         /**
9087          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9088          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9089          * @return {Roo.Element} The new child
9090          */
9091         insertFirst: function(el, returnDom){
9092             el = el || {};
9093             if(typeof el == 'object' && !el.nodeType){ // dh config
9094                 return this.createChild(el, this.dom.firstChild, returnDom);
9095             }else{
9096                 el = Roo.getDom(el);
9097                 this.dom.insertBefore(el, this.dom.firstChild);
9098                 return !returnDom ? Roo.get(el) : el;
9099             }
9100         },
9101
9102         /**
9103          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9104          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9105          * @param {String} where (optional) 'before' or 'after' defaults to before
9106          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9107          * @return {Roo.Element} the inserted Element
9108          */
9109         insertSibling: function(el, where, returnDom){
9110             where = where ? where.toLowerCase() : 'before';
9111             el = el || {};
9112             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9113
9114             if(typeof el == 'object' && !el.nodeType){ // dh config
9115                 if(where == 'after' && !this.dom.nextSibling){
9116                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9117                 }else{
9118                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9119                 }
9120
9121             }else{
9122                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9123                             where == 'before' ? this.dom : this.dom.nextSibling);
9124                 if(!returnDom){
9125                     rt = Roo.get(rt);
9126                 }
9127             }
9128             return rt;
9129         },
9130
9131         /**
9132          * Creates and wraps this element with another element
9133          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9134          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9135          * @return {HTMLElement/Element} The newly created wrapper element
9136          */
9137         wrap: function(config, returnDom){
9138             if(!config){
9139                 config = {tag: "div"};
9140             }
9141             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9142             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9143             return newEl;
9144         },
9145
9146         /**
9147          * Replaces the passed element with this element
9148          * @param {String/HTMLElement/Element} el The element to replace
9149          * @return {Roo.Element} this
9150          */
9151         replace: function(el){
9152             el = Roo.get(el);
9153             this.insertBefore(el);
9154             el.remove();
9155             return this;
9156         },
9157
9158         /**
9159          * Inserts an html fragment into this element
9160          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9161          * @param {String} html The HTML fragment
9162          * @param {Boolean} returnEl True to return an Roo.Element
9163          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9164          */
9165         insertHtml : function(where, html, returnEl){
9166             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9167             return returnEl ? Roo.get(el) : el;
9168         },
9169
9170         /**
9171          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9172          * @param {Object} o The object with the attributes
9173          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9174          * @return {Roo.Element} this
9175          */
9176         set : function(o, useSet){
9177             var el = this.dom;
9178             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9179             for(var attr in o){
9180                 if(attr == "style" || typeof o[attr] == "function") continue;
9181                 if(attr=="cls"){
9182                     el.className = o["cls"];
9183                 }else{
9184                     if(useSet) el.setAttribute(attr, o[attr]);
9185                     else el[attr] = o[attr];
9186                 }
9187             }
9188             if(o.style){
9189                 Roo.DomHelper.applyStyles(el, o.style);
9190             }
9191             return this;
9192         },
9193
9194         /**
9195          * Convenience method for constructing a KeyMap
9196          * @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:
9197          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9198          * @param {Function} fn The function to call
9199          * @param {Object} scope (optional) The scope of the function
9200          * @return {Roo.KeyMap} The KeyMap created
9201          */
9202         addKeyListener : function(key, fn, scope){
9203             var config;
9204             if(typeof key != "object" || key instanceof Array){
9205                 config = {
9206                     key: key,
9207                     fn: fn,
9208                     scope: scope
9209                 };
9210             }else{
9211                 config = {
9212                     key : key.key,
9213                     shift : key.shift,
9214                     ctrl : key.ctrl,
9215                     alt : key.alt,
9216                     fn: fn,
9217                     scope: scope
9218                 };
9219             }
9220             return new Roo.KeyMap(this, config);
9221         },
9222
9223         /**
9224          * Creates a KeyMap for this element
9225          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9226          * @return {Roo.KeyMap} The KeyMap created
9227          */
9228         addKeyMap : function(config){
9229             return new Roo.KeyMap(this, config);
9230         },
9231
9232         /**
9233          * Returns true if this element is scrollable.
9234          * @return {Boolean}
9235          */
9236          isScrollable : function(){
9237             var dom = this.dom;
9238             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9239         },
9240
9241         /**
9242          * 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().
9243          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9244          * @param {Number} value The new scroll value
9245          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9246          * @return {Element} this
9247          */
9248
9249         scrollTo : function(side, value, animate){
9250             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9251             if(!animate || !A){
9252                 this.dom[prop] = value;
9253             }else{
9254                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9255                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9256             }
9257             return this;
9258         },
9259
9260         /**
9261          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9262          * within this element's scrollable range.
9263          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9264          * @param {Number} distance How far to scroll the element in pixels
9265          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9266          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9267          * was scrolled as far as it could go.
9268          */
9269          scroll : function(direction, distance, animate){
9270              if(!this.isScrollable()){
9271                  return;
9272              }
9273              var el = this.dom;
9274              var l = el.scrollLeft, t = el.scrollTop;
9275              var w = el.scrollWidth, h = el.scrollHeight;
9276              var cw = el.clientWidth, ch = el.clientHeight;
9277              direction = direction.toLowerCase();
9278              var scrolled = false;
9279              var a = this.preanim(arguments, 2);
9280              switch(direction){
9281                  case "l":
9282                  case "left":
9283                      if(w - l > cw){
9284                          var v = Math.min(l + distance, w-cw);
9285                          this.scrollTo("left", v, a);
9286                          scrolled = true;
9287                      }
9288                      break;
9289                 case "r":
9290                 case "right":
9291                      if(l > 0){
9292                          var v = Math.max(l - distance, 0);
9293                          this.scrollTo("left", v, a);
9294                          scrolled = true;
9295                      }
9296                      break;
9297                 case "t":
9298                 case "top":
9299                 case "up":
9300                      if(t > 0){
9301                          var v = Math.max(t - distance, 0);
9302                          this.scrollTo("top", v, a);
9303                          scrolled = true;
9304                      }
9305                      break;
9306                 case "b":
9307                 case "bottom":
9308                 case "down":
9309                      if(h - t > ch){
9310                          var v = Math.min(t + distance, h-ch);
9311                          this.scrollTo("top", v, a);
9312                          scrolled = true;
9313                      }
9314                      break;
9315              }
9316              return scrolled;
9317         },
9318
9319         /**
9320          * Translates the passed page coordinates into left/top css values for this element
9321          * @param {Number/Array} x The page x or an array containing [x, y]
9322          * @param {Number} y The page y
9323          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9324          */
9325         translatePoints : function(x, y){
9326             if(typeof x == 'object' || x instanceof Array){
9327                 y = x[1]; x = x[0];
9328             }
9329             var p = this.getStyle('position');
9330             var o = this.getXY();
9331
9332             var l = parseInt(this.getStyle('left'), 10);
9333             var t = parseInt(this.getStyle('top'), 10);
9334
9335             if(isNaN(l)){
9336                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9337             }
9338             if(isNaN(t)){
9339                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9340             }
9341
9342             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9343         },
9344
9345         /**
9346          * Returns the current scroll position of the element.
9347          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9348          */
9349         getScroll : function(){
9350             var d = this.dom, doc = document;
9351             if(d == doc || d == doc.body){
9352                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9353                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9354                 return {left: l, top: t};
9355             }else{
9356                 return {left: d.scrollLeft, top: d.scrollTop};
9357             }
9358         },
9359
9360         /**
9361          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9362          * are convert to standard 6 digit hex color.
9363          * @param {String} attr The css attribute
9364          * @param {String} defaultValue The default value to use when a valid color isn't found
9365          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9366          * YUI color anims.
9367          */
9368         getColor : function(attr, defaultValue, prefix){
9369             var v = this.getStyle(attr);
9370             if(!v || v == "transparent" || v == "inherit") {
9371                 return defaultValue;
9372             }
9373             var color = typeof prefix == "undefined" ? "#" : prefix;
9374             if(v.substr(0, 4) == "rgb("){
9375                 var rvs = v.slice(4, v.length -1).split(",");
9376                 for(var i = 0; i < 3; i++){
9377                     var h = parseInt(rvs[i]).toString(16);
9378                     if(h < 16){
9379                         h = "0" + h;
9380                     }
9381                     color += h;
9382                 }
9383             } else {
9384                 if(v.substr(0, 1) == "#"){
9385                     if(v.length == 4) {
9386                         for(var i = 1; i < 4; i++){
9387                             var c = v.charAt(i);
9388                             color +=  c + c;
9389                         }
9390                     }else if(v.length == 7){
9391                         color += v.substr(1);
9392                     }
9393                 }
9394             }
9395             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9396         },
9397
9398         /**
9399          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9400          * gradient background, rounded corners and a 4-way shadow.
9401          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9402          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9403          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9404          * @return {Roo.Element} this
9405          */
9406         boxWrap : function(cls){
9407             cls = cls || 'x-box';
9408             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9409             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9410             return el;
9411         },
9412
9413         /**
9414          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9415          * @param {String} namespace The namespace in which to look for the attribute
9416          * @param {String} name The attribute name
9417          * @return {String} The attribute value
9418          */
9419         getAttributeNS : Roo.isIE ? function(ns, name){
9420             var d = this.dom;
9421             var type = typeof d[ns+":"+name];
9422             if(type != 'undefined' && type != 'unknown'){
9423                 return d[ns+":"+name];
9424             }
9425             return d[name];
9426         } : function(ns, name){
9427             var d = this.dom;
9428             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9429         }
9430     };
9431
9432     var ep = El.prototype;
9433
9434     /**
9435      * Appends an event handler (Shorthand for addListener)
9436      * @param {String}   eventName     The type of event to append
9437      * @param {Function} fn        The method the event invokes
9438      * @param {Object} scope       (optional) The scope (this object) of the fn
9439      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9440      * @method
9441      */
9442     ep.on = ep.addListener;
9443         // backwards compat
9444     ep.mon = ep.addListener;
9445
9446     /**
9447      * Removes an event handler from this element (shorthand for removeListener)
9448      * @param {String} eventName the type of event to remove
9449      * @param {Function} fn the method the event invokes
9450      * @return {Roo.Element} this
9451      * @method
9452      */
9453     ep.un = ep.removeListener;
9454
9455     /**
9456      * true to automatically adjust width and height settings for box-model issues (default to true)
9457      */
9458     ep.autoBoxAdjust = true;
9459
9460     // private
9461     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9462
9463     // private
9464     El.addUnits = function(v, defaultUnit){
9465         if(v === "" || v == "auto"){
9466             return v;
9467         }
9468         if(v === undefined){
9469             return '';
9470         }
9471         if(typeof v == "number" || !El.unitPattern.test(v)){
9472             return v + (defaultUnit || 'px');
9473         }
9474         return v;
9475     };
9476
9477     // special markup used throughout Roo when box wrapping elements
9478     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>';
9479     /**
9480      * Visibility mode constant - Use visibility to hide element
9481      * @static
9482      * @type Number
9483      */
9484     El.VISIBILITY = 1;
9485     /**
9486      * Visibility mode constant - Use display to hide element
9487      * @static
9488      * @type Number
9489      */
9490     El.DISPLAY = 2;
9491
9492     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9493     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9494     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9495
9496
9497
9498     /**
9499      * @private
9500      */
9501     El.cache = {};
9502
9503     var docEl;
9504
9505     /**
9506      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9507      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9508      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9509      * @return {Element} The Element object
9510      * @static
9511      */
9512     El.get = function(el){
9513         var ex, elm, id;
9514         if(!el){ return null; }
9515         if(typeof el == "string"){ // element id
9516             if(!(elm = document.getElementById(el))){
9517                 return null;
9518             }
9519             if(ex = El.cache[el]){
9520                 ex.dom = elm;
9521             }else{
9522                 ex = El.cache[el] = new El(elm);
9523             }
9524             return ex;
9525         }else if(el.tagName){ // dom element
9526             if(!(id = el.id)){
9527                 id = Roo.id(el);
9528             }
9529             if(ex = El.cache[id]){
9530                 ex.dom = el;
9531             }else{
9532                 ex = El.cache[id] = new El(el);
9533             }
9534             return ex;
9535         }else if(el instanceof El){
9536             if(el != docEl){
9537                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9538                                                               // catch case where it hasn't been appended
9539                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9540             }
9541             return el;
9542         }else if(el.isComposite){
9543             return el;
9544         }else if(el instanceof Array){
9545             return El.select(el);
9546         }else if(el == document){
9547             // create a bogus element object representing the document object
9548             if(!docEl){
9549                 var f = function(){};
9550                 f.prototype = El.prototype;
9551                 docEl = new f();
9552                 docEl.dom = document;
9553             }
9554             return docEl;
9555         }
9556         return null;
9557     };
9558
9559     // private
9560     El.uncache = function(el){
9561         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9562             if(a[i]){
9563                 delete El.cache[a[i].id || a[i]];
9564             }
9565         }
9566     };
9567
9568     // private
9569     // Garbage collection - uncache elements/purge listeners on orphaned elements
9570     // so we don't hold a reference and cause the browser to retain them
9571     El.garbageCollect = function(){
9572         if(!Roo.enableGarbageCollector){
9573             clearInterval(El.collectorThread);
9574             return;
9575         }
9576         for(var eid in El.cache){
9577             var el = El.cache[eid], d = el.dom;
9578             // -------------------------------------------------------
9579             // Determining what is garbage:
9580             // -------------------------------------------------------
9581             // !d
9582             // dom node is null, definitely garbage
9583             // -------------------------------------------------------
9584             // !d.parentNode
9585             // no parentNode == direct orphan, definitely garbage
9586             // -------------------------------------------------------
9587             // !d.offsetParent && !document.getElementById(eid)
9588             // display none elements have no offsetParent so we will
9589             // also try to look it up by it's id. However, check
9590             // offsetParent first so we don't do unneeded lookups.
9591             // This enables collection of elements that are not orphans
9592             // directly, but somewhere up the line they have an orphan
9593             // parent.
9594             // -------------------------------------------------------
9595             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9596                 delete El.cache[eid];
9597                 if(d && Roo.enableListenerCollection){
9598                     E.purgeElement(d);
9599                 }
9600             }
9601         }
9602     }
9603     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9604
9605
9606     // dom is optional
9607     El.Flyweight = function(dom){
9608         this.dom = dom;
9609     };
9610     El.Flyweight.prototype = El.prototype;
9611
9612     El._flyweights = {};
9613     /**
9614      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9615      * the dom node can be overwritten by other code.
9616      * @param {String/HTMLElement} el The dom node or id
9617      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9618      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9619      * @static
9620      * @return {Element} The shared Element object
9621      */
9622     El.fly = function(el, named){
9623         named = named || '_global';
9624         el = Roo.getDom(el);
9625         if(!el){
9626             return null;
9627         }
9628         if(!El._flyweights[named]){
9629             El._flyweights[named] = new El.Flyweight();
9630         }
9631         El._flyweights[named].dom = el;
9632         return El._flyweights[named];
9633     };
9634
9635     /**
9636      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9637      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9638      * Shorthand of {@link Roo.Element#get}
9639      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9640      * @return {Element} The Element object
9641      * @member Roo
9642      * @method get
9643      */
9644     Roo.get = El.get;
9645     /**
9646      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9647      * the dom node can be overwritten by other code.
9648      * Shorthand of {@link Roo.Element#fly}
9649      * @param {String/HTMLElement} el The dom node or id
9650      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9651      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9652      * @static
9653      * @return {Element} The shared Element object
9654      * @member Roo
9655      * @method fly
9656      */
9657     Roo.fly = El.fly;
9658
9659     // speedy lookup for elements never to box adjust
9660     var noBoxAdjust = Roo.isStrict ? {
9661         select:1
9662     } : {
9663         input:1, select:1, textarea:1
9664     };
9665     if(Roo.isIE || Roo.isGecko){
9666         noBoxAdjust['button'] = 1;
9667     }
9668
9669
9670     Roo.EventManager.on(window, 'unload', function(){
9671         delete El.cache;
9672         delete El._flyweights;
9673     });
9674 })();
9675
9676
9677
9678
9679 if(Roo.DomQuery){
9680     Roo.Element.selectorFunction = Roo.DomQuery.select;
9681 }
9682
9683 Roo.Element.select = function(selector, unique, root){
9684     var els;
9685     if(typeof selector == "string"){
9686         els = Roo.Element.selectorFunction(selector, root);
9687     }else if(selector.length !== undefined){
9688         els = selector;
9689     }else{
9690         throw "Invalid selector";
9691     }
9692     if(unique === true){
9693         return new Roo.CompositeElement(els);
9694     }else{
9695         return new Roo.CompositeElementLite(els);
9696     }
9697 };
9698 /**
9699  * Selects elements based on the passed CSS selector to enable working on them as 1.
9700  * @param {String/Array} selector The CSS selector or an array of elements
9701  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9702  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9703  * @return {CompositeElementLite/CompositeElement}
9704  * @member Roo
9705  * @method select
9706  */
9707 Roo.select = Roo.Element.select;
9708
9709
9710
9711
9712
9713
9714
9715
9716
9717
9718
9719
9720
9721
9722 /*
9723  * Based on:
9724  * Ext JS Library 1.1.1
9725  * Copyright(c) 2006-2007, Ext JS, LLC.
9726  *
9727  * Originally Released Under LGPL - original licence link has changed is not relivant.
9728  *
9729  * Fork - LGPL
9730  * <script type="text/javascript">
9731  */
9732
9733
9734
9735 //Notifies Element that fx methods are available
9736 Roo.enableFx = true;
9737
9738 /**
9739  * @class Roo.Fx
9740  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9741  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9742  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9743  * Element effects to work.</p><br/>
9744  *
9745  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9746  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9747  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9748  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9749  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9750  * expected results and should be done with care.</p><br/>
9751  *
9752  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9753  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9754 <pre>
9755 Value  Description
9756 -----  -----------------------------
9757 tl     The top left corner
9758 t      The center of the top edge
9759 tr     The top right corner
9760 l      The center of the left edge
9761 r      The center of the right edge
9762 bl     The bottom left corner
9763 b      The center of the bottom edge
9764 br     The bottom right corner
9765 </pre>
9766  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9767  * below are common options that can be passed to any Fx method.</b>
9768  * @cfg {Function} callback A function called when the effect is finished
9769  * @cfg {Object} scope The scope of the effect function
9770  * @cfg {String} easing A valid Easing value for the effect
9771  * @cfg {String} afterCls A css class to apply after the effect
9772  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9773  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9774  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9775  * effects that end with the element being visually hidden, ignored otherwise)
9776  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9777  * a function which returns such a specification that will be applied to the Element after the effect finishes
9778  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9779  * @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
9780  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9781  */
9782 Roo.Fx = {
9783         /**
9784          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9785          * origin for the slide effect.  This function automatically handles wrapping the element with
9786          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9787          * Usage:
9788          *<pre><code>
9789 // default: slide the element in from the top
9790 el.slideIn();
9791
9792 // custom: slide the element in from the right with a 2-second duration
9793 el.slideIn('r', { duration: 2 });
9794
9795 // common config options shown with default values
9796 el.slideIn('t', {
9797     easing: 'easeOut',
9798     duration: .5
9799 });
9800 </code></pre>
9801          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9802          * @param {Object} options (optional) Object literal with any of the Fx config options
9803          * @return {Roo.Element} The Element
9804          */
9805     slideIn : function(anchor, o){
9806         var el = this.getFxEl();
9807         o = o || {};
9808
9809         el.queueFx(o, function(){
9810
9811             anchor = anchor || "t";
9812
9813             // fix display to visibility
9814             this.fixDisplay();
9815
9816             // restore values after effect
9817             var r = this.getFxRestore();
9818             var b = this.getBox();
9819             // fixed size for slide
9820             this.setSize(b);
9821
9822             // wrap if needed
9823             var wrap = this.fxWrap(r.pos, o, "hidden");
9824
9825             var st = this.dom.style;
9826             st.visibility = "visible";
9827             st.position = "absolute";
9828
9829             // clear out temp styles after slide and unwrap
9830             var after = function(){
9831                 el.fxUnwrap(wrap, r.pos, o);
9832                 st.width = r.width;
9833                 st.height = r.height;
9834                 el.afterFx(o);
9835             };
9836             // time to calc the positions
9837             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9838
9839             switch(anchor.toLowerCase()){
9840                 case "t":
9841                     wrap.setSize(b.width, 0);
9842                     st.left = st.bottom = "0";
9843                     a = {height: bh};
9844                 break;
9845                 case "l":
9846                     wrap.setSize(0, b.height);
9847                     st.right = st.top = "0";
9848                     a = {width: bw};
9849                 break;
9850                 case "r":
9851                     wrap.setSize(0, b.height);
9852                     wrap.setX(b.right);
9853                     st.left = st.top = "0";
9854                     a = {width: bw, points: pt};
9855                 break;
9856                 case "b":
9857                     wrap.setSize(b.width, 0);
9858                     wrap.setY(b.bottom);
9859                     st.left = st.top = "0";
9860                     a = {height: bh, points: pt};
9861                 break;
9862                 case "tl":
9863                     wrap.setSize(0, 0);
9864                     st.right = st.bottom = "0";
9865                     a = {width: bw, height: bh};
9866                 break;
9867                 case "bl":
9868                     wrap.setSize(0, 0);
9869                     wrap.setY(b.y+b.height);
9870                     st.right = st.top = "0";
9871                     a = {width: bw, height: bh, points: pt};
9872                 break;
9873                 case "br":
9874                     wrap.setSize(0, 0);
9875                     wrap.setXY([b.right, b.bottom]);
9876                     st.left = st.top = "0";
9877                     a = {width: bw, height: bh, points: pt};
9878                 break;
9879                 case "tr":
9880                     wrap.setSize(0, 0);
9881                     wrap.setX(b.x+b.width);
9882                     st.left = st.bottom = "0";
9883                     a = {width: bw, height: bh, points: pt};
9884                 break;
9885             }
9886             this.dom.style.visibility = "visible";
9887             wrap.show();
9888
9889             arguments.callee.anim = wrap.fxanim(a,
9890                 o,
9891                 'motion',
9892                 .5,
9893                 'easeOut', after);
9894         });
9895         return this;
9896     },
9897     
9898         /**
9899          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9900          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9901          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9902          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9903          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9904          * Usage:
9905          *<pre><code>
9906 // default: slide the element out to the top
9907 el.slideOut();
9908
9909 // custom: slide the element out to the right with a 2-second duration
9910 el.slideOut('r', { duration: 2 });
9911
9912 // common config options shown with default values
9913 el.slideOut('t', {
9914     easing: 'easeOut',
9915     duration: .5,
9916     remove: false,
9917     useDisplay: false
9918 });
9919 </code></pre>
9920          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9921          * @param {Object} options (optional) Object literal with any of the Fx config options
9922          * @return {Roo.Element} The Element
9923          */
9924     slideOut : function(anchor, o){
9925         var el = this.getFxEl();
9926         o = o || {};
9927
9928         el.queueFx(o, function(){
9929
9930             anchor = anchor || "t";
9931
9932             // restore values after effect
9933             var r = this.getFxRestore();
9934             
9935             var b = this.getBox();
9936             // fixed size for slide
9937             this.setSize(b);
9938
9939             // wrap if needed
9940             var wrap = this.fxWrap(r.pos, o, "visible");
9941
9942             var st = this.dom.style;
9943             st.visibility = "visible";
9944             st.position = "absolute";
9945
9946             wrap.setSize(b);
9947
9948             var after = function(){
9949                 if(o.useDisplay){
9950                     el.setDisplayed(false);
9951                 }else{
9952                     el.hide();
9953                 }
9954
9955                 el.fxUnwrap(wrap, r.pos, o);
9956
9957                 st.width = r.width;
9958                 st.height = r.height;
9959
9960                 el.afterFx(o);
9961             };
9962
9963             var a, zero = {to: 0};
9964             switch(anchor.toLowerCase()){
9965                 case "t":
9966                     st.left = st.bottom = "0";
9967                     a = {height: zero};
9968                 break;
9969                 case "l":
9970                     st.right = st.top = "0";
9971                     a = {width: zero};
9972                 break;
9973                 case "r":
9974                     st.left = st.top = "0";
9975                     a = {width: zero, points: {to:[b.right, b.y]}};
9976                 break;
9977                 case "b":
9978                     st.left = st.top = "0";
9979                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9980                 break;
9981                 case "tl":
9982                     st.right = st.bottom = "0";
9983                     a = {width: zero, height: zero};
9984                 break;
9985                 case "bl":
9986                     st.right = st.top = "0";
9987                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9988                 break;
9989                 case "br":
9990                     st.left = st.top = "0";
9991                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9992                 break;
9993                 case "tr":
9994                     st.left = st.bottom = "0";
9995                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9996                 break;
9997             }
9998
9999             arguments.callee.anim = wrap.fxanim(a,
10000                 o,
10001                 'motion',
10002                 .5,
10003                 "easeOut", after);
10004         });
10005         return this;
10006     },
10007
10008         /**
10009          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10010          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10011          * The element must be removed from the DOM using the 'remove' config option if desired.
10012          * Usage:
10013          *<pre><code>
10014 // default
10015 el.puff();
10016
10017 // common config options shown with default values
10018 el.puff({
10019     easing: 'easeOut',
10020     duration: .5,
10021     remove: false,
10022     useDisplay: false
10023 });
10024 </code></pre>
10025          * @param {Object} options (optional) Object literal with any of the Fx config options
10026          * @return {Roo.Element} The Element
10027          */
10028     puff : function(o){
10029         var el = this.getFxEl();
10030         o = o || {};
10031
10032         el.queueFx(o, function(){
10033             this.clearOpacity();
10034             this.show();
10035
10036             // restore values after effect
10037             var r = this.getFxRestore();
10038             var st = this.dom.style;
10039
10040             var after = function(){
10041                 if(o.useDisplay){
10042                     el.setDisplayed(false);
10043                 }else{
10044                     el.hide();
10045                 }
10046
10047                 el.clearOpacity();
10048
10049                 el.setPositioning(r.pos);
10050                 st.width = r.width;
10051                 st.height = r.height;
10052                 st.fontSize = '';
10053                 el.afterFx(o);
10054             };
10055
10056             var width = this.getWidth();
10057             var height = this.getHeight();
10058
10059             arguments.callee.anim = this.fxanim({
10060                     width : {to: this.adjustWidth(width * 2)},
10061                     height : {to: this.adjustHeight(height * 2)},
10062                     points : {by: [-(width * .5), -(height * .5)]},
10063                     opacity : {to: 0},
10064                     fontSize: {to:200, unit: "%"}
10065                 },
10066                 o,
10067                 'motion',
10068                 .5,
10069                 "easeOut", after);
10070         });
10071         return this;
10072     },
10073
10074         /**
10075          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10076          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10077          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10078          * Usage:
10079          *<pre><code>
10080 // default
10081 el.switchOff();
10082
10083 // all config options shown with default values
10084 el.switchOff({
10085     easing: 'easeIn',
10086     duration: .3,
10087     remove: false,
10088     useDisplay: false
10089 });
10090 </code></pre>
10091          * @param {Object} options (optional) Object literal with any of the Fx config options
10092          * @return {Roo.Element} The Element
10093          */
10094     switchOff : function(o){
10095         var el = this.getFxEl();
10096         o = o || {};
10097
10098         el.queueFx(o, function(){
10099             this.clearOpacity();
10100             this.clip();
10101
10102             // restore values after effect
10103             var r = this.getFxRestore();
10104             var st = this.dom.style;
10105
10106             var after = function(){
10107                 if(o.useDisplay){
10108                     el.setDisplayed(false);
10109                 }else{
10110                     el.hide();
10111                 }
10112
10113                 el.clearOpacity();
10114                 el.setPositioning(r.pos);
10115                 st.width = r.width;
10116                 st.height = r.height;
10117
10118                 el.afterFx(o);
10119             };
10120
10121             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10122                 this.clearOpacity();
10123                 (function(){
10124                     this.fxanim({
10125                         height:{to:1},
10126                         points:{by:[0, this.getHeight() * .5]}
10127                     }, o, 'motion', 0.3, 'easeIn', after);
10128                 }).defer(100, this);
10129             });
10130         });
10131         return this;
10132     },
10133
10134     /**
10135      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10136      * changed using the "attr" config option) and then fading back to the original color. If no original
10137      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10138      * Usage:
10139 <pre><code>
10140 // default: highlight background to yellow
10141 el.highlight();
10142
10143 // custom: highlight foreground text to blue for 2 seconds
10144 el.highlight("0000ff", { attr: 'color', duration: 2 });
10145
10146 // common config options shown with default values
10147 el.highlight("ffff9c", {
10148     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10149     endColor: (current color) or "ffffff",
10150     easing: 'easeIn',
10151     duration: 1
10152 });
10153 </code></pre>
10154      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10155      * @param {Object} options (optional) Object literal with any of the Fx config options
10156      * @return {Roo.Element} The Element
10157      */ 
10158     highlight : function(color, o){
10159         var el = this.getFxEl();
10160         o = o || {};
10161
10162         el.queueFx(o, function(){
10163             color = color || "ffff9c";
10164             attr = o.attr || "backgroundColor";
10165
10166             this.clearOpacity();
10167             this.show();
10168
10169             var origColor = this.getColor(attr);
10170             var restoreColor = this.dom.style[attr];
10171             endColor = (o.endColor || origColor) || "ffffff";
10172
10173             var after = function(){
10174                 el.dom.style[attr] = restoreColor;
10175                 el.afterFx(o);
10176             };
10177
10178             var a = {};
10179             a[attr] = {from: color, to: endColor};
10180             arguments.callee.anim = this.fxanim(a,
10181                 o,
10182                 'color',
10183                 1,
10184                 'easeIn', after);
10185         });
10186         return this;
10187     },
10188
10189    /**
10190     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10191     * Usage:
10192 <pre><code>
10193 // default: a single light blue ripple
10194 el.frame();
10195
10196 // custom: 3 red ripples lasting 3 seconds total
10197 el.frame("ff0000", 3, { duration: 3 });
10198
10199 // common config options shown with default values
10200 el.frame("C3DAF9", 1, {
10201     duration: 1 //duration of entire animation (not each individual ripple)
10202     // Note: Easing is not configurable and will be ignored if included
10203 });
10204 </code></pre>
10205     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10206     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10207     * @param {Object} options (optional) Object literal with any of the Fx config options
10208     * @return {Roo.Element} The Element
10209     */
10210     frame : function(color, count, o){
10211         var el = this.getFxEl();
10212         o = o || {};
10213
10214         el.queueFx(o, function(){
10215             color = color || "#C3DAF9";
10216             if(color.length == 6){
10217                 color = "#" + color;
10218             }
10219             count = count || 1;
10220             duration = o.duration || 1;
10221             this.show();
10222
10223             var b = this.getBox();
10224             var animFn = function(){
10225                 var proxy = this.createProxy({
10226
10227                      style:{
10228                         visbility:"hidden",
10229                         position:"absolute",
10230                         "z-index":"35000", // yee haw
10231                         border:"0px solid " + color
10232                      }
10233                   });
10234                 var scale = Roo.isBorderBox ? 2 : 1;
10235                 proxy.animate({
10236                     top:{from:b.y, to:b.y - 20},
10237                     left:{from:b.x, to:b.x - 20},
10238                     borderWidth:{from:0, to:10},
10239                     opacity:{from:1, to:0},
10240                     height:{from:b.height, to:(b.height + (20*scale))},
10241                     width:{from:b.width, to:(b.width + (20*scale))}
10242                 }, duration, function(){
10243                     proxy.remove();
10244                 });
10245                 if(--count > 0){
10246                      animFn.defer((duration/2)*1000, this);
10247                 }else{
10248                     el.afterFx(o);
10249                 }
10250             };
10251             animFn.call(this);
10252         });
10253         return this;
10254     },
10255
10256    /**
10257     * Creates a pause before any subsequent queued effects begin.  If there are
10258     * no effects queued after the pause it will have no effect.
10259     * Usage:
10260 <pre><code>
10261 el.pause(1);
10262 </code></pre>
10263     * @param {Number} seconds The length of time to pause (in seconds)
10264     * @return {Roo.Element} The Element
10265     */
10266     pause : function(seconds){
10267         var el = this.getFxEl();
10268         var o = {};
10269
10270         el.queueFx(o, function(){
10271             setTimeout(function(){
10272                 el.afterFx(o);
10273             }, seconds * 1000);
10274         });
10275         return this;
10276     },
10277
10278    /**
10279     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10280     * using the "endOpacity" config option.
10281     * Usage:
10282 <pre><code>
10283 // default: fade in from opacity 0 to 100%
10284 el.fadeIn();
10285
10286 // custom: fade in from opacity 0 to 75% over 2 seconds
10287 el.fadeIn({ endOpacity: .75, duration: 2});
10288
10289 // common config options shown with default values
10290 el.fadeIn({
10291     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10292     easing: 'easeOut',
10293     duration: .5
10294 });
10295 </code></pre>
10296     * @param {Object} options (optional) Object literal with any of the Fx config options
10297     * @return {Roo.Element} The Element
10298     */
10299     fadeIn : function(o){
10300         var el = this.getFxEl();
10301         o = o || {};
10302         el.queueFx(o, function(){
10303             this.setOpacity(0);
10304             this.fixDisplay();
10305             this.dom.style.visibility = 'visible';
10306             var to = o.endOpacity || 1;
10307             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10308                 o, null, .5, "easeOut", function(){
10309                 if(to == 1){
10310                     this.clearOpacity();
10311                 }
10312                 el.afterFx(o);
10313             });
10314         });
10315         return this;
10316     },
10317
10318    /**
10319     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10320     * using the "endOpacity" config option.
10321     * Usage:
10322 <pre><code>
10323 // default: fade out from the element's current opacity to 0
10324 el.fadeOut();
10325
10326 // custom: fade out from the element's current opacity to 25% over 2 seconds
10327 el.fadeOut({ endOpacity: .25, duration: 2});
10328
10329 // common config options shown with default values
10330 el.fadeOut({
10331     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10332     easing: 'easeOut',
10333     duration: .5
10334     remove: false,
10335     useDisplay: false
10336 });
10337 </code></pre>
10338     * @param {Object} options (optional) Object literal with any of the Fx config options
10339     * @return {Roo.Element} The Element
10340     */
10341     fadeOut : function(o){
10342         var el = this.getFxEl();
10343         o = o || {};
10344         el.queueFx(o, function(){
10345             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10346                 o, null, .5, "easeOut", function(){
10347                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10348                      this.dom.style.display = "none";
10349                 }else{
10350                      this.dom.style.visibility = "hidden";
10351                 }
10352                 this.clearOpacity();
10353                 el.afterFx(o);
10354             });
10355         });
10356         return this;
10357     },
10358
10359    /**
10360     * Animates the transition of an element's dimensions from a starting height/width
10361     * to an ending height/width.
10362     * Usage:
10363 <pre><code>
10364 // change height and width to 100x100 pixels
10365 el.scale(100, 100);
10366
10367 // common config options shown with default values.  The height and width will default to
10368 // the element's existing values if passed as null.
10369 el.scale(
10370     [element's width],
10371     [element's height], {
10372     easing: 'easeOut',
10373     duration: .35
10374 });
10375 </code></pre>
10376     * @param {Number} width  The new width (pass undefined to keep the original width)
10377     * @param {Number} height  The new height (pass undefined to keep the original height)
10378     * @param {Object} options (optional) Object literal with any of the Fx config options
10379     * @return {Roo.Element} The Element
10380     */
10381     scale : function(w, h, o){
10382         this.shift(Roo.apply({}, o, {
10383             width: w,
10384             height: h
10385         }));
10386         return this;
10387     },
10388
10389    /**
10390     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10391     * Any of these properties not specified in the config object will not be changed.  This effect 
10392     * requires that at least one new dimension, position or opacity setting must be passed in on
10393     * the config object in order for the function to have any effect.
10394     * Usage:
10395 <pre><code>
10396 // slide the element horizontally to x position 200 while changing the height and opacity
10397 el.shift({ x: 200, height: 50, opacity: .8 });
10398
10399 // common config options shown with default values.
10400 el.shift({
10401     width: [element's width],
10402     height: [element's height],
10403     x: [element's x position],
10404     y: [element's y position],
10405     opacity: [element's opacity],
10406     easing: 'easeOut',
10407     duration: .35
10408 });
10409 </code></pre>
10410     * @param {Object} options  Object literal with any of the Fx config options
10411     * @return {Roo.Element} The Element
10412     */
10413     shift : function(o){
10414         var el = this.getFxEl();
10415         o = o || {};
10416         el.queueFx(o, function(){
10417             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10418             if(w !== undefined){
10419                 a.width = {to: this.adjustWidth(w)};
10420             }
10421             if(h !== undefined){
10422                 a.height = {to: this.adjustHeight(h)};
10423             }
10424             if(x !== undefined || y !== undefined){
10425                 a.points = {to: [
10426                     x !== undefined ? x : this.getX(),
10427                     y !== undefined ? y : this.getY()
10428                 ]};
10429             }
10430             if(op !== undefined){
10431                 a.opacity = {to: op};
10432             }
10433             if(o.xy !== undefined){
10434                 a.points = {to: o.xy};
10435             }
10436             arguments.callee.anim = this.fxanim(a,
10437                 o, 'motion', .35, "easeOut", function(){
10438                 el.afterFx(o);
10439             });
10440         });
10441         return this;
10442     },
10443
10444         /**
10445          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10446          * ending point of the effect.
10447          * Usage:
10448          *<pre><code>
10449 // default: slide the element downward while fading out
10450 el.ghost();
10451
10452 // custom: slide the element out to the right with a 2-second duration
10453 el.ghost('r', { duration: 2 });
10454
10455 // common config options shown with default values
10456 el.ghost('b', {
10457     easing: 'easeOut',
10458     duration: .5
10459     remove: false,
10460     useDisplay: false
10461 });
10462 </code></pre>
10463          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10464          * @param {Object} options (optional) Object literal with any of the Fx config options
10465          * @return {Roo.Element} The Element
10466          */
10467     ghost : function(anchor, o){
10468         var el = this.getFxEl();
10469         o = o || {};
10470
10471         el.queueFx(o, function(){
10472             anchor = anchor || "b";
10473
10474             // restore values after effect
10475             var r = this.getFxRestore();
10476             var w = this.getWidth(),
10477                 h = this.getHeight();
10478
10479             var st = this.dom.style;
10480
10481             var after = function(){
10482                 if(o.useDisplay){
10483                     el.setDisplayed(false);
10484                 }else{
10485                     el.hide();
10486                 }
10487
10488                 el.clearOpacity();
10489                 el.setPositioning(r.pos);
10490                 st.width = r.width;
10491                 st.height = r.height;
10492
10493                 el.afterFx(o);
10494             };
10495
10496             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10497             switch(anchor.toLowerCase()){
10498                 case "t":
10499                     pt.by = [0, -h];
10500                 break;
10501                 case "l":
10502                     pt.by = [-w, 0];
10503                 break;
10504                 case "r":
10505                     pt.by = [w, 0];
10506                 break;
10507                 case "b":
10508                     pt.by = [0, h];
10509                 break;
10510                 case "tl":
10511                     pt.by = [-w, -h];
10512                 break;
10513                 case "bl":
10514                     pt.by = [-w, h];
10515                 break;
10516                 case "br":
10517                     pt.by = [w, h];
10518                 break;
10519                 case "tr":
10520                     pt.by = [w, -h];
10521                 break;
10522             }
10523
10524             arguments.callee.anim = this.fxanim(a,
10525                 o,
10526                 'motion',
10527                 .5,
10528                 "easeOut", after);
10529         });
10530         return this;
10531     },
10532
10533         /**
10534          * Ensures that all effects queued after syncFx is called on the element are
10535          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10536          * @return {Roo.Element} The Element
10537          */
10538     syncFx : function(){
10539         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10540             block : false,
10541             concurrent : true,
10542             stopFx : false
10543         });
10544         return this;
10545     },
10546
10547         /**
10548          * Ensures that all effects queued after sequenceFx is called on the element are
10549          * run in sequence.  This is the opposite of {@link #syncFx}.
10550          * @return {Roo.Element} The Element
10551          */
10552     sequenceFx : function(){
10553         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10554             block : false,
10555             concurrent : false,
10556             stopFx : false
10557         });
10558         return this;
10559     },
10560
10561         /* @private */
10562     nextFx : function(){
10563         var ef = this.fxQueue[0];
10564         if(ef){
10565             ef.call(this);
10566         }
10567     },
10568
10569         /**
10570          * Returns true if the element has any effects actively running or queued, else returns false.
10571          * @return {Boolean} True if element has active effects, else false
10572          */
10573     hasActiveFx : function(){
10574         return this.fxQueue && this.fxQueue[0];
10575     },
10576
10577         /**
10578          * Stops any running effects and clears the element's internal effects queue if it contains
10579          * any additional effects that haven't started yet.
10580          * @return {Roo.Element} The Element
10581          */
10582     stopFx : function(){
10583         if(this.hasActiveFx()){
10584             var cur = this.fxQueue[0];
10585             if(cur && cur.anim && cur.anim.isAnimated()){
10586                 this.fxQueue = [cur]; // clear out others
10587                 cur.anim.stop(true);
10588             }
10589         }
10590         return this;
10591     },
10592
10593         /* @private */
10594     beforeFx : function(o){
10595         if(this.hasActiveFx() && !o.concurrent){
10596            if(o.stopFx){
10597                this.stopFx();
10598                return true;
10599            }
10600            return false;
10601         }
10602         return true;
10603     },
10604
10605         /**
10606          * Returns true if the element is currently blocking so that no other effect can be queued
10607          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10608          * used to ensure that an effect initiated by a user action runs to completion prior to the
10609          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10610          * @return {Boolean} True if blocking, else false
10611          */
10612     hasFxBlock : function(){
10613         var q = this.fxQueue;
10614         return q && q[0] && q[0].block;
10615     },
10616
10617         /* @private */
10618     queueFx : function(o, fn){
10619         if(!this.fxQueue){
10620             this.fxQueue = [];
10621         }
10622         if(!this.hasFxBlock()){
10623             Roo.applyIf(o, this.fxDefaults);
10624             if(!o.concurrent){
10625                 var run = this.beforeFx(o);
10626                 fn.block = o.block;
10627                 this.fxQueue.push(fn);
10628                 if(run){
10629                     this.nextFx();
10630                 }
10631             }else{
10632                 fn.call(this);
10633             }
10634         }
10635         return this;
10636     },
10637
10638         /* @private */
10639     fxWrap : function(pos, o, vis){
10640         var wrap;
10641         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10642             var wrapXY;
10643             if(o.fixPosition){
10644                 wrapXY = this.getXY();
10645             }
10646             var div = document.createElement("div");
10647             div.style.visibility = vis;
10648             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10649             wrap.setPositioning(pos);
10650             if(wrap.getStyle("position") == "static"){
10651                 wrap.position("relative");
10652             }
10653             this.clearPositioning('auto');
10654             wrap.clip();
10655             wrap.dom.appendChild(this.dom);
10656             if(wrapXY){
10657                 wrap.setXY(wrapXY);
10658             }
10659         }
10660         return wrap;
10661     },
10662
10663         /* @private */
10664     fxUnwrap : function(wrap, pos, o){
10665         this.clearPositioning();
10666         this.setPositioning(pos);
10667         if(!o.wrap){
10668             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10669             wrap.remove();
10670         }
10671     },
10672
10673         /* @private */
10674     getFxRestore : function(){
10675         var st = this.dom.style;
10676         return {pos: this.getPositioning(), width: st.width, height : st.height};
10677     },
10678
10679         /* @private */
10680     afterFx : function(o){
10681         if(o.afterStyle){
10682             this.applyStyles(o.afterStyle);
10683         }
10684         if(o.afterCls){
10685             this.addClass(o.afterCls);
10686         }
10687         if(o.remove === true){
10688             this.remove();
10689         }
10690         Roo.callback(o.callback, o.scope, [this]);
10691         if(!o.concurrent){
10692             this.fxQueue.shift();
10693             this.nextFx();
10694         }
10695     },
10696
10697         /* @private */
10698     getFxEl : function(){ // support for composite element fx
10699         return Roo.get(this.dom);
10700     },
10701
10702         /* @private */
10703     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10704         animType = animType || 'run';
10705         opt = opt || {};
10706         var anim = Roo.lib.Anim[animType](
10707             this.dom, args,
10708             (opt.duration || defaultDur) || .35,
10709             (opt.easing || defaultEase) || 'easeOut',
10710             function(){
10711                 Roo.callback(cb, this);
10712             },
10713             this
10714         );
10715         opt.anim = anim;
10716         return anim;
10717     }
10718 };
10719
10720 // backwords compat
10721 Roo.Fx.resize = Roo.Fx.scale;
10722
10723 //When included, Roo.Fx is automatically applied to Element so that all basic
10724 //effects are available directly via the Element API
10725 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10726  * Based on:
10727  * Ext JS Library 1.1.1
10728  * Copyright(c) 2006-2007, Ext JS, LLC.
10729  *
10730  * Originally Released Under LGPL - original licence link has changed is not relivant.
10731  *
10732  * Fork - LGPL
10733  * <script type="text/javascript">
10734  */
10735
10736
10737 /**
10738  * @class Roo.CompositeElement
10739  * Standard composite class. Creates a Roo.Element for every element in the collection.
10740  * <br><br>
10741  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10742  * actions will be performed on all the elements in this collection.</b>
10743  * <br><br>
10744  * All methods return <i>this</i> and can be chained.
10745  <pre><code>
10746  var els = Roo.select("#some-el div.some-class", true);
10747  // or select directly from an existing element
10748  var el = Roo.get('some-el');
10749  el.select('div.some-class', true);
10750
10751  els.setWidth(100); // all elements become 100 width
10752  els.hide(true); // all elements fade out and hide
10753  // or
10754  els.setWidth(100).hide(true);
10755  </code></pre>
10756  */
10757 Roo.CompositeElement = function(els){
10758     this.elements = [];
10759     this.addElements(els);
10760 };
10761 Roo.CompositeElement.prototype = {
10762     isComposite: true,
10763     addElements : function(els){
10764         if(!els) return this;
10765         if(typeof els == "string"){
10766             els = Roo.Element.selectorFunction(els);
10767         }
10768         var yels = this.elements;
10769         var index = yels.length-1;
10770         for(var i = 0, len = els.length; i < len; i++) {
10771                 yels[++index] = Roo.get(els[i]);
10772         }
10773         return this;
10774     },
10775
10776     /**
10777     * Clears this composite and adds the elements returned by the passed selector.
10778     * @param {String/Array} els A string CSS selector, an array of elements or an element
10779     * @return {CompositeElement} this
10780     */
10781     fill : function(els){
10782         this.elements = [];
10783         this.add(els);
10784         return this;
10785     },
10786
10787     /**
10788     * Filters this composite to only elements that match the passed selector.
10789     * @param {String} selector A string CSS selector
10790     * @return {CompositeElement} this
10791     */
10792     filter : function(selector){
10793         var els = [];
10794         this.each(function(el){
10795             if(el.is(selector)){
10796                 els[els.length] = el.dom;
10797             }
10798         });
10799         this.fill(els);
10800         return this;
10801     },
10802
10803     invoke : function(fn, args){
10804         var els = this.elements;
10805         for(var i = 0, len = els.length; i < len; i++) {
10806                 Roo.Element.prototype[fn].apply(els[i], args);
10807         }
10808         return this;
10809     },
10810     /**
10811     * Adds elements to this composite.
10812     * @param {String/Array} els A string CSS selector, an array of elements or an element
10813     * @return {CompositeElement} this
10814     */
10815     add : function(els){
10816         if(typeof els == "string"){
10817             this.addElements(Roo.Element.selectorFunction(els));
10818         }else if(els.length !== undefined){
10819             this.addElements(els);
10820         }else{
10821             this.addElements([els]);
10822         }
10823         return this;
10824     },
10825     /**
10826     * Calls the passed function passing (el, this, index) for each element in this composite.
10827     * @param {Function} fn The function to call
10828     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10829     * @return {CompositeElement} this
10830     */
10831     each : function(fn, scope){
10832         var els = this.elements;
10833         for(var i = 0, len = els.length; i < len; i++){
10834             if(fn.call(scope || els[i], els[i], this, i) === false) {
10835                 break;
10836             }
10837         }
10838         return this;
10839     },
10840
10841     /**
10842      * Returns the Element object at the specified index
10843      * @param {Number} index
10844      * @return {Roo.Element}
10845      */
10846     item : function(index){
10847         return this.elements[index] || null;
10848     },
10849
10850     /**
10851      * Returns the first Element
10852      * @return {Roo.Element}
10853      */
10854     first : function(){
10855         return this.item(0);
10856     },
10857
10858     /**
10859      * Returns the last Element
10860      * @return {Roo.Element}
10861      */
10862     last : function(){
10863         return this.item(this.elements.length-1);
10864     },
10865
10866     /**
10867      * Returns the number of elements in this composite
10868      * @return Number
10869      */
10870     getCount : function(){
10871         return this.elements.length;
10872     },
10873
10874     /**
10875      * Returns true if this composite contains the passed element
10876      * @return Boolean
10877      */
10878     contains : function(el){
10879         return this.indexOf(el) !== -1;
10880     },
10881
10882     /**
10883      * Returns true if this composite contains the passed element
10884      * @return Boolean
10885      */
10886     indexOf : function(el){
10887         return this.elements.indexOf(Roo.get(el));
10888     },
10889
10890
10891     /**
10892     * Removes the specified element(s).
10893     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10894     * or an array of any of those.
10895     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10896     * @return {CompositeElement} this
10897     */
10898     removeElement : function(el, removeDom){
10899         if(el instanceof Array){
10900             for(var i = 0, len = el.length; i < len; i++){
10901                 this.removeElement(el[i]);
10902             }
10903             return this;
10904         }
10905         var index = typeof el == 'number' ? el : this.indexOf(el);
10906         if(index !== -1){
10907             if(removeDom){
10908                 var d = this.elements[index];
10909                 if(d.dom){
10910                     d.remove();
10911                 }else{
10912                     d.parentNode.removeChild(d);
10913                 }
10914             }
10915             this.elements.splice(index, 1);
10916         }
10917         return this;
10918     },
10919
10920     /**
10921     * Replaces the specified element with the passed element.
10922     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10923     * to replace.
10924     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10925     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10926     * @return {CompositeElement} this
10927     */
10928     replaceElement : function(el, replacement, domReplace){
10929         var index = typeof el == 'number' ? el : this.indexOf(el);
10930         if(index !== -1){
10931             if(domReplace){
10932                 this.elements[index].replaceWith(replacement);
10933             }else{
10934                 this.elements.splice(index, 1, Roo.get(replacement))
10935             }
10936         }
10937         return this;
10938     },
10939
10940     /**
10941      * Removes all elements.
10942      */
10943     clear : function(){
10944         this.elements = [];
10945     }
10946 };
10947 (function(){
10948     Roo.CompositeElement.createCall = function(proto, fnName){
10949         if(!proto[fnName]){
10950             proto[fnName] = function(){
10951                 return this.invoke(fnName, arguments);
10952             };
10953         }
10954     };
10955     for(var fnName in Roo.Element.prototype){
10956         if(typeof Roo.Element.prototype[fnName] == "function"){
10957             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10958         }
10959     };
10960 })();
10961 /*
10962  * Based on:
10963  * Ext JS Library 1.1.1
10964  * Copyright(c) 2006-2007, Ext JS, LLC.
10965  *
10966  * Originally Released Under LGPL - original licence link has changed is not relivant.
10967  *
10968  * Fork - LGPL
10969  * <script type="text/javascript">
10970  */
10971
10972 /**
10973  * @class Roo.CompositeElementLite
10974  * @extends Roo.CompositeElement
10975  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10976  <pre><code>
10977  var els = Roo.select("#some-el div.some-class");
10978  // or select directly from an existing element
10979  var el = Roo.get('some-el');
10980  el.select('div.some-class');
10981
10982  els.setWidth(100); // all elements become 100 width
10983  els.hide(true); // all elements fade out and hide
10984  // or
10985  els.setWidth(100).hide(true);
10986  </code></pre><br><br>
10987  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10988  * actions will be performed on all the elements in this collection.</b>
10989  */
10990 Roo.CompositeElementLite = function(els){
10991     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10992     this.el = new Roo.Element.Flyweight();
10993 };
10994 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10995     addElements : function(els){
10996         if(els){
10997             if(els instanceof Array){
10998                 this.elements = this.elements.concat(els);
10999             }else{
11000                 var yels = this.elements;
11001                 var index = yels.length-1;
11002                 for(var i = 0, len = els.length; i < len; i++) {
11003                     yels[++index] = els[i];
11004                 }
11005             }
11006         }
11007         return this;
11008     },
11009     invoke : function(fn, args){
11010         var els = this.elements;
11011         var el = this.el;
11012         for(var i = 0, len = els.length; i < len; i++) {
11013             el.dom = els[i];
11014                 Roo.Element.prototype[fn].apply(el, args);
11015         }
11016         return this;
11017     },
11018     /**
11019      * Returns a flyweight Element of the dom element object at the specified index
11020      * @param {Number} index
11021      * @return {Roo.Element}
11022      */
11023     item : function(index){
11024         if(!this.elements[index]){
11025             return null;
11026         }
11027         this.el.dom = this.elements[index];
11028         return this.el;
11029     },
11030
11031     // fixes scope with flyweight
11032     addListener : function(eventName, handler, scope, opt){
11033         var els = this.elements;
11034         for(var i = 0, len = els.length; i < len; i++) {
11035             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11036         }
11037         return this;
11038     },
11039
11040     /**
11041     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11042     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11043     * a reference to the dom node, use el.dom.</b>
11044     * @param {Function} fn The function to call
11045     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11046     * @return {CompositeElement} this
11047     */
11048     each : function(fn, scope){
11049         var els = this.elements;
11050         var el = this.el;
11051         for(var i = 0, len = els.length; i < len; i++){
11052             el.dom = els[i];
11053                 if(fn.call(scope || el, el, this, i) === false){
11054                 break;
11055             }
11056         }
11057         return this;
11058     },
11059
11060     indexOf : function(el){
11061         return this.elements.indexOf(Roo.getDom(el));
11062     },
11063
11064     replaceElement : function(el, replacement, domReplace){
11065         var index = typeof el == 'number' ? el : this.indexOf(el);
11066         if(index !== -1){
11067             replacement = Roo.getDom(replacement);
11068             if(domReplace){
11069                 var d = this.elements[index];
11070                 d.parentNode.insertBefore(replacement, d);
11071                 d.parentNode.removeChild(d);
11072             }
11073             this.elements.splice(index, 1, replacement);
11074         }
11075         return this;
11076     }
11077 });
11078 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11079
11080 /*
11081  * Based on:
11082  * Ext JS Library 1.1.1
11083  * Copyright(c) 2006-2007, Ext JS, LLC.
11084  *
11085  * Originally Released Under LGPL - original licence link has changed is not relivant.
11086  *
11087  * Fork - LGPL
11088  * <script type="text/javascript">
11089  */
11090
11091  
11092
11093 /**
11094  * @class Roo.data.Connection
11095  * @extends Roo.util.Observable
11096  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11097  * either to a configured URL, or to a URL specified at request time.<br><br>
11098  * <p>
11099  * Requests made by this class are asynchronous, and will return immediately. No data from
11100  * the server will be available to the statement immediately following the {@link #request} call.
11101  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11102  * <p>
11103  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11104  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11105  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11106  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11107  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11108  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11109  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11110  * standard DOM methods.
11111  * @constructor
11112  * @param {Object} config a configuration object.
11113  */
11114 Roo.data.Connection = function(config){
11115     Roo.apply(this, config);
11116     this.addEvents({
11117         /**
11118          * @event beforerequest
11119          * Fires before a network request is made to retrieve a data object.
11120          * @param {Connection} conn This Connection object.
11121          * @param {Object} options The options config object passed to the {@link #request} method.
11122          */
11123         "beforerequest" : true,
11124         /**
11125          * @event requestcomplete
11126          * Fires if the request was successfully completed.
11127          * @param {Connection} conn This Connection object.
11128          * @param {Object} response The XHR object containing the response data.
11129          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11130          * @param {Object} options The options config object passed to the {@link #request} method.
11131          */
11132         "requestcomplete" : true,
11133         /**
11134          * @event requestexception
11135          * Fires if an error HTTP status was returned from the server.
11136          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11137          * @param {Connection} conn This Connection object.
11138          * @param {Object} response The XHR object containing the response data.
11139          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11140          * @param {Object} options The options config object passed to the {@link #request} method.
11141          */
11142         "requestexception" : true
11143     });
11144     Roo.data.Connection.superclass.constructor.call(this);
11145 };
11146
11147 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11148     /**
11149      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11150      */
11151     /**
11152      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11153      * extra parameters to each request made by this object. (defaults to undefined)
11154      */
11155     /**
11156      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11157      *  to each request made by this object. (defaults to undefined)
11158      */
11159     /**
11160      * @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)
11161      */
11162     /**
11163      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11164      */
11165     timeout : 30000,
11166     /**
11167      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11168      * @type Boolean
11169      */
11170     autoAbort:false,
11171
11172     /**
11173      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11174      * @type Boolean
11175      */
11176     disableCaching: true,
11177
11178     /**
11179      * Sends an HTTP request to a remote server.
11180      * @param {Object} options An object which may contain the following properties:<ul>
11181      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11182      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11183      * request, a url encoded string or a function to call to get either.</li>
11184      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11185      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11186      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11187      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11188      * <li>options {Object} The parameter to the request call.</li>
11189      * <li>success {Boolean} True if the request succeeded.</li>
11190      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11191      * </ul></li>
11192      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11193      * The callback is passed the following parameters:<ul>
11194      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11195      * <li>options {Object} The parameter to the request call.</li>
11196      * </ul></li>
11197      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11198      * The callback is passed the following parameters:<ul>
11199      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11200      * <li>options {Object} The parameter to the request call.</li>
11201      * </ul></li>
11202      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11203      * for the callback function. Defaults to the browser window.</li>
11204      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11205      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11206      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11207      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11208      * params for the post data. Any params will be appended to the URL.</li>
11209      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11210      * </ul>
11211      * @return {Number} transactionId
11212      */
11213     request : function(o){
11214         if(this.fireEvent("beforerequest", this, o) !== false){
11215             var p = o.params;
11216
11217             if(typeof p == "function"){
11218                 p = p.call(o.scope||window, o);
11219             }
11220             if(typeof p == "object"){
11221                 p = Roo.urlEncode(o.params);
11222             }
11223             if(this.extraParams){
11224                 var extras = Roo.urlEncode(this.extraParams);
11225                 p = p ? (p + '&' + extras) : extras;
11226             }
11227
11228             var url = o.url || this.url;
11229             if(typeof url == 'function'){
11230                 url = url.call(o.scope||window, o);
11231             }
11232
11233             if(o.form){
11234                 var form = Roo.getDom(o.form);
11235                 url = url || form.action;
11236
11237                 var enctype = form.getAttribute("enctype");
11238                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11239                     return this.doFormUpload(o, p, url);
11240                 }
11241                 var f = Roo.lib.Ajax.serializeForm(form);
11242                 p = p ? (p + '&' + f) : f;
11243             }
11244
11245             var hs = o.headers;
11246             if(this.defaultHeaders){
11247                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11248                 if(!o.headers){
11249                     o.headers = hs;
11250                 }
11251             }
11252
11253             var cb = {
11254                 success: this.handleResponse,
11255                 failure: this.handleFailure,
11256                 scope: this,
11257                 argument: {options: o},
11258                 timeout : this.timeout
11259             };
11260
11261             var method = o.method||this.method||(p ? "POST" : "GET");
11262
11263             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11264                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11265             }
11266
11267             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11268                 if(o.autoAbort){
11269                     this.abort();
11270                 }
11271             }else if(this.autoAbort !== false){
11272                 this.abort();
11273             }
11274
11275             if((method == 'GET' && p) || o.xmlData){
11276                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11277                 p = '';
11278             }
11279             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11280             return this.transId;
11281         }else{
11282             Roo.callback(o.callback, o.scope, [o, null, null]);
11283             return null;
11284         }
11285     },
11286
11287     /**
11288      * Determine whether this object has a request outstanding.
11289      * @param {Number} transactionId (Optional) defaults to the last transaction
11290      * @return {Boolean} True if there is an outstanding request.
11291      */
11292     isLoading : function(transId){
11293         if(transId){
11294             return Roo.lib.Ajax.isCallInProgress(transId);
11295         }else{
11296             return this.transId ? true : false;
11297         }
11298     },
11299
11300     /**
11301      * Aborts any outstanding request.
11302      * @param {Number} transactionId (Optional) defaults to the last transaction
11303      */
11304     abort : function(transId){
11305         if(transId || this.isLoading()){
11306             Roo.lib.Ajax.abort(transId || this.transId);
11307         }
11308     },
11309
11310     // private
11311     handleResponse : function(response){
11312         this.transId = false;
11313         var options = response.argument.options;
11314         response.argument = options ? options.argument : null;
11315         this.fireEvent("requestcomplete", this, response, options);
11316         Roo.callback(options.success, options.scope, [response, options]);
11317         Roo.callback(options.callback, options.scope, [options, true, response]);
11318     },
11319
11320     // private
11321     handleFailure : function(response, e){
11322         this.transId = false;
11323         var options = response.argument.options;
11324         response.argument = options ? options.argument : null;
11325         this.fireEvent("requestexception", this, response, options, e);
11326         Roo.callback(options.failure, options.scope, [response, options]);
11327         Roo.callback(options.callback, options.scope, [options, false, response]);
11328     },
11329
11330     // private
11331     doFormUpload : function(o, ps, url){
11332         var id = Roo.id();
11333         var frame = document.createElement('iframe');
11334         frame.id = id;
11335         frame.name = id;
11336         frame.className = 'x-hidden';
11337         if(Roo.isIE){
11338             frame.src = Roo.SSL_SECURE_URL;
11339         }
11340         document.body.appendChild(frame);
11341
11342         if(Roo.isIE){
11343            document.frames[id].name = id;
11344         }
11345
11346         var form = Roo.getDom(o.form);
11347         form.target = id;
11348         form.method = 'POST';
11349         form.enctype = form.encoding = 'multipart/form-data';
11350         if(url){
11351             form.action = url;
11352         }
11353
11354         var hiddens, hd;
11355         if(ps){ // add dynamic params
11356             hiddens = [];
11357             ps = Roo.urlDecode(ps, false);
11358             for(var k in ps){
11359                 if(ps.hasOwnProperty(k)){
11360                     hd = document.createElement('input');
11361                     hd.type = 'hidden';
11362                     hd.name = k;
11363                     hd.value = ps[k];
11364                     form.appendChild(hd);
11365                     hiddens.push(hd);
11366                 }
11367             }
11368         }
11369
11370         function cb(){
11371             var r = {  // bogus response object
11372                 responseText : '',
11373                 responseXML : null
11374             };
11375
11376             r.argument = o ? o.argument : null;
11377
11378             try { //
11379                 var doc;
11380                 if(Roo.isIE){
11381                     doc = frame.contentWindow.document;
11382                 }else {
11383                     doc = (frame.contentDocument || window.frames[id].document);
11384                 }
11385                 if(doc && doc.body){
11386                     r.responseText = doc.body.innerHTML;
11387                 }
11388                 if(doc && doc.XMLDocument){
11389                     r.responseXML = doc.XMLDocument;
11390                 }else {
11391                     r.responseXML = doc;
11392                 }
11393             }
11394             catch(e) {
11395                 // ignore
11396             }
11397
11398             Roo.EventManager.removeListener(frame, 'load', cb, this);
11399
11400             this.fireEvent("requestcomplete", this, r, o);
11401             Roo.callback(o.success, o.scope, [r, o]);
11402             Roo.callback(o.callback, o.scope, [o, true, r]);
11403
11404             setTimeout(function(){document.body.removeChild(frame);}, 100);
11405         }
11406
11407         Roo.EventManager.on(frame, 'load', cb, this);
11408         form.submit();
11409
11410         if(hiddens){ // remove dynamic params
11411             for(var i = 0, len = hiddens.length; i < len; i++){
11412                 form.removeChild(hiddens[i]);
11413             }
11414         }
11415     }
11416 });
11417
11418 /**
11419  * @class Roo.Ajax
11420  * @extends Roo.data.Connection
11421  * Global Ajax request class.
11422  *
11423  * @singleton
11424  */
11425 Roo.Ajax = new Roo.data.Connection({
11426     // fix up the docs
11427    /**
11428      * @cfg {String} url @hide
11429      */
11430     /**
11431      * @cfg {Object} extraParams @hide
11432      */
11433     /**
11434      * @cfg {Object} defaultHeaders @hide
11435      */
11436     /**
11437      * @cfg {String} method (Optional) @hide
11438      */
11439     /**
11440      * @cfg {Number} timeout (Optional) @hide
11441      */
11442     /**
11443      * @cfg {Boolean} autoAbort (Optional) @hide
11444      */
11445
11446     /**
11447      * @cfg {Boolean} disableCaching (Optional) @hide
11448      */
11449
11450     /**
11451      * @property  disableCaching
11452      * True to add a unique cache-buster param to GET requests. (defaults to true)
11453      * @type Boolean
11454      */
11455     /**
11456      * @property  url
11457      * The default URL to be used for requests to the server. (defaults to undefined)
11458      * @type String
11459      */
11460     /**
11461      * @property  extraParams
11462      * An object containing properties which are used as
11463      * extra parameters to each request made by this object. (defaults to undefined)
11464      * @type Object
11465      */
11466     /**
11467      * @property  defaultHeaders
11468      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11469      * @type Object
11470      */
11471     /**
11472      * @property  method
11473      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11474      * @type String
11475      */
11476     /**
11477      * @property  timeout
11478      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11479      * @type Number
11480      */
11481
11482     /**
11483      * @property  autoAbort
11484      * Whether a new request should abort any pending requests. (defaults to false)
11485      * @type Boolean
11486      */
11487     autoAbort : false,
11488
11489     /**
11490      * Serialize the passed form into a url encoded string
11491      * @param {String/HTMLElement} form
11492      * @return {String}
11493      */
11494     serializeForm : function(form){
11495         return Roo.lib.Ajax.serializeForm(form);
11496     }
11497 });/*
11498  * Based on:
11499  * Ext JS Library 1.1.1
11500  * Copyright(c) 2006-2007, Ext JS, LLC.
11501  *
11502  * Originally Released Under LGPL - original licence link has changed is not relivant.
11503  *
11504  * Fork - LGPL
11505  * <script type="text/javascript">
11506  */
11507  
11508 /**
11509  * @class Roo.Ajax
11510  * @extends Roo.data.Connection
11511  * Global Ajax request class.
11512  *
11513  * @instanceOf  Roo.data.Connection
11514  */
11515 Roo.Ajax = new Roo.data.Connection({
11516     // fix up the docs
11517     
11518     /**
11519      * fix up scoping
11520      * @scope Roo.Ajax
11521      */
11522     
11523    /**
11524      * @cfg {String} url @hide
11525      */
11526     /**
11527      * @cfg {Object} extraParams @hide
11528      */
11529     /**
11530      * @cfg {Object} defaultHeaders @hide
11531      */
11532     /**
11533      * @cfg {String} method (Optional) @hide
11534      */
11535     /**
11536      * @cfg {Number} timeout (Optional) @hide
11537      */
11538     /**
11539      * @cfg {Boolean} autoAbort (Optional) @hide
11540      */
11541
11542     /**
11543      * @cfg {Boolean} disableCaching (Optional) @hide
11544      */
11545
11546     /**
11547      * @property  disableCaching
11548      * True to add a unique cache-buster param to GET requests. (defaults to true)
11549      * @type Boolean
11550      */
11551     /**
11552      * @property  url
11553      * The default URL to be used for requests to the server. (defaults to undefined)
11554      * @type String
11555      */
11556     /**
11557      * @property  extraParams
11558      * An object containing properties which are used as
11559      * extra parameters to each request made by this object. (defaults to undefined)
11560      * @type Object
11561      */
11562     /**
11563      * @property  defaultHeaders
11564      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11565      * @type Object
11566      */
11567     /**
11568      * @property  method
11569      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11570      * @type String
11571      */
11572     /**
11573      * @property  timeout
11574      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11575      * @type Number
11576      */
11577
11578     /**
11579      * @property  autoAbort
11580      * Whether a new request should abort any pending requests. (defaults to false)
11581      * @type Boolean
11582      */
11583     autoAbort : false,
11584
11585     /**
11586      * Serialize the passed form into a url encoded string
11587      * @param {String/HTMLElement} form
11588      * @return {String}
11589      */
11590     serializeForm : function(form){
11591         return Roo.lib.Ajax.serializeForm(form);
11592     }
11593 });/*
11594  * Based on:
11595  * Ext JS Library 1.1.1
11596  * Copyright(c) 2006-2007, Ext JS, LLC.
11597  *
11598  * Originally Released Under LGPL - original licence link has changed is not relivant.
11599  *
11600  * Fork - LGPL
11601  * <script type="text/javascript">
11602  */
11603
11604  
11605 /**
11606  * @class Roo.UpdateManager
11607  * @extends Roo.util.Observable
11608  * Provides AJAX-style update for Element object.<br><br>
11609  * Usage:<br>
11610  * <pre><code>
11611  * // Get it from a Roo.Element object
11612  * var el = Roo.get("foo");
11613  * var mgr = el.getUpdateManager();
11614  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11615  * ...
11616  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11617  * <br>
11618  * // or directly (returns the same UpdateManager instance)
11619  * var mgr = new Roo.UpdateManager("myElementId");
11620  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11621  * mgr.on("update", myFcnNeedsToKnow);
11622  * <br>
11623    // short handed call directly from the element object
11624    Roo.get("foo").load({
11625         url: "bar.php",
11626         scripts:true,
11627         params: "for=bar",
11628         text: "Loading Foo..."
11629    });
11630  * </code></pre>
11631  * @constructor
11632  * Create new UpdateManager directly.
11633  * @param {String/HTMLElement/Roo.Element} el The element to update
11634  * @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).
11635  */
11636 Roo.UpdateManager = function(el, forceNew){
11637     el = Roo.get(el);
11638     if(!forceNew && el.updateManager){
11639         return el.updateManager;
11640     }
11641     /**
11642      * The Element object
11643      * @type Roo.Element
11644      */
11645     this.el = el;
11646     /**
11647      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11648      * @type String
11649      */
11650     this.defaultUrl = null;
11651
11652     this.addEvents({
11653         /**
11654          * @event beforeupdate
11655          * Fired before an update is made, return false from your handler and the update is cancelled.
11656          * @param {Roo.Element} el
11657          * @param {String/Object/Function} url
11658          * @param {String/Object} params
11659          */
11660         "beforeupdate": true,
11661         /**
11662          * @event update
11663          * Fired after successful update is made.
11664          * @param {Roo.Element} el
11665          * @param {Object} oResponseObject The response Object
11666          */
11667         "update": true,
11668         /**
11669          * @event failure
11670          * Fired on update failure.
11671          * @param {Roo.Element} el
11672          * @param {Object} oResponseObject The response Object
11673          */
11674         "failure": true
11675     });
11676     var d = Roo.UpdateManager.defaults;
11677     /**
11678      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11679      * @type String
11680      */
11681     this.sslBlankUrl = d.sslBlankUrl;
11682     /**
11683      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11684      * @type Boolean
11685      */
11686     this.disableCaching = d.disableCaching;
11687     /**
11688      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11689      * @type String
11690      */
11691     this.indicatorText = d.indicatorText;
11692     /**
11693      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11694      * @type String
11695      */
11696     this.showLoadIndicator = d.showLoadIndicator;
11697     /**
11698      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11699      * @type Number
11700      */
11701     this.timeout = d.timeout;
11702
11703     /**
11704      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11705      * @type Boolean
11706      */
11707     this.loadScripts = d.loadScripts;
11708
11709     /**
11710      * Transaction object of current executing transaction
11711      */
11712     this.transaction = null;
11713
11714     /**
11715      * @private
11716      */
11717     this.autoRefreshProcId = null;
11718     /**
11719      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11720      * @type Function
11721      */
11722     this.refreshDelegate = this.refresh.createDelegate(this);
11723     /**
11724      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11725      * @type Function
11726      */
11727     this.updateDelegate = this.update.createDelegate(this);
11728     /**
11729      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11730      * @type Function
11731      */
11732     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11733     /**
11734      * @private
11735      */
11736     this.successDelegate = this.processSuccess.createDelegate(this);
11737     /**
11738      * @private
11739      */
11740     this.failureDelegate = this.processFailure.createDelegate(this);
11741
11742     if(!this.renderer){
11743      /**
11744       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11745       */
11746     this.renderer = new Roo.UpdateManager.BasicRenderer();
11747     }
11748     
11749     Roo.UpdateManager.superclass.constructor.call(this);
11750 };
11751
11752 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11753     /**
11754      * Get the Element this UpdateManager is bound to
11755      * @return {Roo.Element} The element
11756      */
11757     getEl : function(){
11758         return this.el;
11759     },
11760     /**
11761      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11762      * @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:
11763 <pre><code>
11764 um.update({<br/>
11765     url: "your-url.php",<br/>
11766     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11767     callback: yourFunction,<br/>
11768     scope: yourObject, //(optional scope)  <br/>
11769     discardUrl: false, <br/>
11770     nocache: false,<br/>
11771     text: "Loading...",<br/>
11772     timeout: 30,<br/>
11773     scripts: false<br/>
11774 });
11775 </code></pre>
11776      * The only required property is url. The optional properties nocache, text and scripts
11777      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11778      * @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}
11779      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11780      * @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.
11781      */
11782     update : function(url, params, callback, discardUrl){
11783         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11784             var method = this.method, cfg;
11785             if(typeof url == "object"){ // must be config object
11786                 cfg = url;
11787                 url = cfg.url;
11788                 params = params || cfg.params;
11789                 callback = callback || cfg.callback;
11790                 discardUrl = discardUrl || cfg.discardUrl;
11791                 if(callback && cfg.scope){
11792                     callback = callback.createDelegate(cfg.scope);
11793                 }
11794                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11795                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11796                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11797                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11798                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11799             }
11800             this.showLoading();
11801             if(!discardUrl){
11802                 this.defaultUrl = url;
11803             }
11804             if(typeof url == "function"){
11805                 url = url.call(this);
11806             }
11807
11808             method = method || (params ? "POST" : "GET");
11809             if(method == "GET"){
11810                 url = this.prepareUrl(url);
11811             }
11812
11813             var o = Roo.apply(cfg ||{}, {
11814                 url : url,
11815                 params: params,
11816                 success: this.successDelegate,
11817                 failure: this.failureDelegate,
11818                 callback: undefined,
11819                 timeout: (this.timeout*1000),
11820                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11821             });
11822
11823             this.transaction = Roo.Ajax.request(o);
11824         }
11825     },
11826
11827     /**
11828      * 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.
11829      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11830      * @param {String/HTMLElement} form The form Id or form element
11831      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11832      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11833      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11834      */
11835     formUpdate : function(form, url, reset, callback){
11836         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11837             if(typeof url == "function"){
11838                 url = url.call(this);
11839             }
11840             form = Roo.getDom(form);
11841             this.transaction = Roo.Ajax.request({
11842                 form: form,
11843                 url:url,
11844                 success: this.successDelegate,
11845                 failure: this.failureDelegate,
11846                 timeout: (this.timeout*1000),
11847                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11848             });
11849             this.showLoading.defer(1, this);
11850         }
11851     },
11852
11853     /**
11854      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11855      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11856      */
11857     refresh : function(callback){
11858         if(this.defaultUrl == null){
11859             return;
11860         }
11861         this.update(this.defaultUrl, null, callback, true);
11862     },
11863
11864     /**
11865      * Set this element to auto refresh.
11866      * @param {Number} interval How often to update (in seconds).
11867      * @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)
11868      * @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}
11869      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11870      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11871      */
11872     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11873         if(refreshNow){
11874             this.update(url || this.defaultUrl, params, callback, true);
11875         }
11876         if(this.autoRefreshProcId){
11877             clearInterval(this.autoRefreshProcId);
11878         }
11879         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11880     },
11881
11882     /**
11883      * Stop auto refresh on this element.
11884      */
11885      stopAutoRefresh : function(){
11886         if(this.autoRefreshProcId){
11887             clearInterval(this.autoRefreshProcId);
11888             delete this.autoRefreshProcId;
11889         }
11890     },
11891
11892     isAutoRefreshing : function(){
11893        return this.autoRefreshProcId ? true : false;
11894     },
11895     /**
11896      * Called to update the element to "Loading" state. Override to perform custom action.
11897      */
11898     showLoading : function(){
11899         if(this.showLoadIndicator){
11900             this.el.update(this.indicatorText);
11901         }
11902     },
11903
11904     /**
11905      * Adds unique parameter to query string if disableCaching = true
11906      * @private
11907      */
11908     prepareUrl : function(url){
11909         if(this.disableCaching){
11910             var append = "_dc=" + (new Date().getTime());
11911             if(url.indexOf("?") !== -1){
11912                 url += "&" + append;
11913             }else{
11914                 url += "?" + append;
11915             }
11916         }
11917         return url;
11918     },
11919
11920     /**
11921      * @private
11922      */
11923     processSuccess : function(response){
11924         this.transaction = null;
11925         if(response.argument.form && response.argument.reset){
11926             try{ // put in try/catch since some older FF releases had problems with this
11927                 response.argument.form.reset();
11928             }catch(e){}
11929         }
11930         if(this.loadScripts){
11931             this.renderer.render(this.el, response, this,
11932                 this.updateComplete.createDelegate(this, [response]));
11933         }else{
11934             this.renderer.render(this.el, response, this);
11935             this.updateComplete(response);
11936         }
11937     },
11938
11939     updateComplete : function(response){
11940         this.fireEvent("update", this.el, response);
11941         if(typeof response.argument.callback == "function"){
11942             response.argument.callback(this.el, true, response);
11943         }
11944     },
11945
11946     /**
11947      * @private
11948      */
11949     processFailure : function(response){
11950         this.transaction = null;
11951         this.fireEvent("failure", this.el, response);
11952         if(typeof response.argument.callback == "function"){
11953             response.argument.callback(this.el, false, response);
11954         }
11955     },
11956
11957     /**
11958      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11959      * @param {Object} renderer The object implementing the render() method
11960      */
11961     setRenderer : function(renderer){
11962         this.renderer = renderer;
11963     },
11964
11965     getRenderer : function(){
11966        return this.renderer;
11967     },
11968
11969     /**
11970      * Set the defaultUrl used for updates
11971      * @param {String/Function} defaultUrl The url or a function to call to get the url
11972      */
11973     setDefaultUrl : function(defaultUrl){
11974         this.defaultUrl = defaultUrl;
11975     },
11976
11977     /**
11978      * Aborts the executing transaction
11979      */
11980     abort : function(){
11981         if(this.transaction){
11982             Roo.Ajax.abort(this.transaction);
11983         }
11984     },
11985
11986     /**
11987      * Returns true if an update is in progress
11988      * @return {Boolean}
11989      */
11990     isUpdating : function(){
11991         if(this.transaction){
11992             return Roo.Ajax.isLoading(this.transaction);
11993         }
11994         return false;
11995     }
11996 });
11997
11998 /**
11999  * @class Roo.UpdateManager.defaults
12000  * @static (not really - but it helps the doc tool)
12001  * The defaults collection enables customizing the default properties of UpdateManager
12002  */
12003    Roo.UpdateManager.defaults = {
12004        /**
12005          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12006          * @type Number
12007          */
12008          timeout : 30,
12009
12010          /**
12011          * True to process scripts by default (Defaults to false).
12012          * @type Boolean
12013          */
12014         loadScripts : false,
12015
12016         /**
12017         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12018         * @type String
12019         */
12020         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12021         /**
12022          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12023          * @type Boolean
12024          */
12025         disableCaching : false,
12026         /**
12027          * Whether to show indicatorText when loading (Defaults to true).
12028          * @type Boolean
12029          */
12030         showLoadIndicator : true,
12031         /**
12032          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12033          * @type String
12034          */
12035         indicatorText : '<div class="loading-indicator">Loading...</div>'
12036    };
12037
12038 /**
12039  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12040  *Usage:
12041  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12042  * @param {String/HTMLElement/Roo.Element} el The element to update
12043  * @param {String} url The url
12044  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12045  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12046  * @static
12047  * @deprecated
12048  * @member Roo.UpdateManager
12049  */
12050 Roo.UpdateManager.updateElement = function(el, url, params, options){
12051     var um = Roo.get(el, true).getUpdateManager();
12052     Roo.apply(um, options);
12053     um.update(url, params, options ? options.callback : null);
12054 };
12055 // alias for backwards compat
12056 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12057 /**
12058  * @class Roo.UpdateManager.BasicRenderer
12059  * Default Content renderer. Updates the elements innerHTML with the responseText.
12060  */
12061 Roo.UpdateManager.BasicRenderer = function(){};
12062
12063 Roo.UpdateManager.BasicRenderer.prototype = {
12064     /**
12065      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12066      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12067      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12068      * @param {Roo.Element} el The element being rendered
12069      * @param {Object} response The YUI Connect response object
12070      * @param {UpdateManager} updateManager The calling update manager
12071      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12072      */
12073      render : function(el, response, updateManager, callback){
12074         el.update(response.responseText, updateManager.loadScripts, callback);
12075     }
12076 };
12077 /*
12078  * Based on:
12079  * Ext JS Library 1.1.1
12080  * Copyright(c) 2006-2007, Ext JS, LLC.
12081  *
12082  * Originally Released Under LGPL - original licence link has changed is not relivant.
12083  *
12084  * Fork - LGPL
12085  * <script type="text/javascript">
12086  */
12087
12088 /**
12089  * @class Roo.util.DelayedTask
12090  * Provides a convenient method of performing setTimeout where a new
12091  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12092  * You can use this class to buffer
12093  * the keypress events for a certain number of milliseconds, and perform only if they stop
12094  * for that amount of time.
12095  * @constructor The parameters to this constructor serve as defaults and are not required.
12096  * @param {Function} fn (optional) The default function to timeout
12097  * @param {Object} scope (optional) The default scope of that timeout
12098  * @param {Array} args (optional) The default Array of arguments
12099  */
12100 Roo.util.DelayedTask = function(fn, scope, args){
12101     var id = null, d, t;
12102
12103     var call = function(){
12104         var now = new Date().getTime();
12105         if(now - t >= d){
12106             clearInterval(id);
12107             id = null;
12108             fn.apply(scope, args || []);
12109         }
12110     };
12111     /**
12112      * Cancels any pending timeout and queues a new one
12113      * @param {Number} delay The milliseconds to delay
12114      * @param {Function} newFn (optional) Overrides function passed to constructor
12115      * @param {Object} newScope (optional) Overrides scope passed to constructor
12116      * @param {Array} newArgs (optional) Overrides args passed to constructor
12117      */
12118     this.delay = function(delay, newFn, newScope, newArgs){
12119         if(id && delay != d){
12120             this.cancel();
12121         }
12122         d = delay;
12123         t = new Date().getTime();
12124         fn = newFn || fn;
12125         scope = newScope || scope;
12126         args = newArgs || args;
12127         if(!id){
12128             id = setInterval(call, d);
12129         }
12130     };
12131
12132     /**
12133      * Cancel the last queued timeout
12134      */
12135     this.cancel = function(){
12136         if(id){
12137             clearInterval(id);
12138             id = null;
12139         }
12140     };
12141 };/*
12142  * Based on:
12143  * Ext JS Library 1.1.1
12144  * Copyright(c) 2006-2007, Ext JS, LLC.
12145  *
12146  * Originally Released Under LGPL - original licence link has changed is not relivant.
12147  *
12148  * Fork - LGPL
12149  * <script type="text/javascript">
12150  */
12151  
12152  
12153 Roo.util.TaskRunner = function(interval){
12154     interval = interval || 10;
12155     var tasks = [], removeQueue = [];
12156     var id = 0;
12157     var running = false;
12158
12159     var stopThread = function(){
12160         running = false;
12161         clearInterval(id);
12162         id = 0;
12163     };
12164
12165     var startThread = function(){
12166         if(!running){
12167             running = true;
12168             id = setInterval(runTasks, interval);
12169         }
12170     };
12171
12172     var removeTask = function(task){
12173         removeQueue.push(task);
12174         if(task.onStop){
12175             task.onStop();
12176         }
12177     };
12178
12179     var runTasks = function(){
12180         if(removeQueue.length > 0){
12181             for(var i = 0, len = removeQueue.length; i < len; i++){
12182                 tasks.remove(removeQueue[i]);
12183             }
12184             removeQueue = [];
12185             if(tasks.length < 1){
12186                 stopThread();
12187                 return;
12188             }
12189         }
12190         var now = new Date().getTime();
12191         for(var i = 0, len = tasks.length; i < len; ++i){
12192             var t = tasks[i];
12193             var itime = now - t.taskRunTime;
12194             if(t.interval <= itime){
12195                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12196                 t.taskRunTime = now;
12197                 if(rt === false || t.taskRunCount === t.repeat){
12198                     removeTask(t);
12199                     return;
12200                 }
12201             }
12202             if(t.duration && t.duration <= (now - t.taskStartTime)){
12203                 removeTask(t);
12204             }
12205         }
12206     };
12207
12208     /**
12209      * Queues a new task.
12210      * @param {Object} task
12211      */
12212     this.start = function(task){
12213         tasks.push(task);
12214         task.taskStartTime = new Date().getTime();
12215         task.taskRunTime = 0;
12216         task.taskRunCount = 0;
12217         startThread();
12218         return task;
12219     };
12220
12221     this.stop = function(task){
12222         removeTask(task);
12223         return task;
12224     };
12225
12226     this.stopAll = function(){
12227         stopThread();
12228         for(var i = 0, len = tasks.length; i < len; i++){
12229             if(tasks[i].onStop){
12230                 tasks[i].onStop();
12231             }
12232         }
12233         tasks = [];
12234         removeQueue = [];
12235     };
12236 };
12237
12238 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12239  * Based on:
12240  * Ext JS Library 1.1.1
12241  * Copyright(c) 2006-2007, Ext JS, LLC.
12242  *
12243  * Originally Released Under LGPL - original licence link has changed is not relivant.
12244  *
12245  * Fork - LGPL
12246  * <script type="text/javascript">
12247  */
12248
12249  
12250 /**
12251  * @class Roo.util.MixedCollection
12252  * @extends Roo.util.Observable
12253  * A Collection class that maintains both numeric indexes and keys and exposes events.
12254  * @constructor
12255  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12256  * collection (defaults to false)
12257  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12258  * and return the key value for that item.  This is used when available to look up the key on items that
12259  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12260  * equivalent to providing an implementation for the {@link #getKey} method.
12261  */
12262 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12263     this.items = [];
12264     this.map = {};
12265     this.keys = [];
12266     this.length = 0;
12267     this.addEvents({
12268         /**
12269          * @event clear
12270          * Fires when the collection is cleared.
12271          */
12272         "clear" : true,
12273         /**
12274          * @event add
12275          * Fires when an item is added to the collection.
12276          * @param {Number} index The index at which the item was added.
12277          * @param {Object} o The item added.
12278          * @param {String} key The key associated with the added item.
12279          */
12280         "add" : true,
12281         /**
12282          * @event replace
12283          * Fires when an item is replaced in the collection.
12284          * @param {String} key he key associated with the new added.
12285          * @param {Object} old The item being replaced.
12286          * @param {Object} new The new item.
12287          */
12288         "replace" : true,
12289         /**
12290          * @event remove
12291          * Fires when an item is removed from the collection.
12292          * @param {Object} o The item being removed.
12293          * @param {String} key (optional) The key associated with the removed item.
12294          */
12295         "remove" : true,
12296         "sort" : true
12297     });
12298     this.allowFunctions = allowFunctions === true;
12299     if(keyFn){
12300         this.getKey = keyFn;
12301     }
12302     Roo.util.MixedCollection.superclass.constructor.call(this);
12303 };
12304
12305 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12306     allowFunctions : false,
12307     
12308 /**
12309  * Adds an item to the collection.
12310  * @param {String} key The key to associate with the item
12311  * @param {Object} o The item to add.
12312  * @return {Object} The item added.
12313  */
12314     add : function(key, o){
12315         if(arguments.length == 1){
12316             o = arguments[0];
12317             key = this.getKey(o);
12318         }
12319         if(typeof key == "undefined" || key === null){
12320             this.length++;
12321             this.items.push(o);
12322             this.keys.push(null);
12323         }else{
12324             var old = this.map[key];
12325             if(old){
12326                 return this.replace(key, o);
12327             }
12328             this.length++;
12329             this.items.push(o);
12330             this.map[key] = o;
12331             this.keys.push(key);
12332         }
12333         this.fireEvent("add", this.length-1, o, key);
12334         return o;
12335     },
12336        
12337 /**
12338   * MixedCollection has a generic way to fetch keys if you implement getKey.
12339 <pre><code>
12340 // normal way
12341 var mc = new Roo.util.MixedCollection();
12342 mc.add(someEl.dom.id, someEl);
12343 mc.add(otherEl.dom.id, otherEl);
12344 //and so on
12345
12346 // using getKey
12347 var mc = new Roo.util.MixedCollection();
12348 mc.getKey = function(el){
12349    return el.dom.id;
12350 };
12351 mc.add(someEl);
12352 mc.add(otherEl);
12353
12354 // or via the constructor
12355 var mc = new Roo.util.MixedCollection(false, function(el){
12356    return el.dom.id;
12357 });
12358 mc.add(someEl);
12359 mc.add(otherEl);
12360 </code></pre>
12361  * @param o {Object} The item for which to find the key.
12362  * @return {Object} The key for the passed item.
12363  */
12364     getKey : function(o){
12365          return o.id; 
12366     },
12367    
12368 /**
12369  * Replaces an item in the collection.
12370  * @param {String} key The key associated with the item to replace, or the item to replace.
12371  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12372  * @return {Object}  The new item.
12373  */
12374     replace : function(key, o){
12375         if(arguments.length == 1){
12376             o = arguments[0];
12377             key = this.getKey(o);
12378         }
12379         var old = this.item(key);
12380         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12381              return this.add(key, o);
12382         }
12383         var index = this.indexOfKey(key);
12384         this.items[index] = o;
12385         this.map[key] = o;
12386         this.fireEvent("replace", key, old, o);
12387         return o;
12388     },
12389    
12390 /**
12391  * Adds all elements of an Array or an Object to the collection.
12392  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12393  * an Array of values, each of which are added to the collection.
12394  */
12395     addAll : function(objs){
12396         if(arguments.length > 1 || objs instanceof Array){
12397             var args = arguments.length > 1 ? arguments : objs;
12398             for(var i = 0, len = args.length; i < len; i++){
12399                 this.add(args[i]);
12400             }
12401         }else{
12402             for(var key in objs){
12403                 if(this.allowFunctions || typeof objs[key] != "function"){
12404                     this.add(key, objs[key]);
12405                 }
12406             }
12407         }
12408     },
12409    
12410 /**
12411  * Executes the specified function once for every item in the collection, passing each
12412  * item as the first and only parameter. returning false from the function will stop the iteration.
12413  * @param {Function} fn The function to execute for each item.
12414  * @param {Object} scope (optional) The scope in which to execute the function.
12415  */
12416     each : function(fn, scope){
12417         var items = [].concat(this.items); // each safe for removal
12418         for(var i = 0, len = items.length; i < len; i++){
12419             if(fn.call(scope || items[i], items[i], i, len) === false){
12420                 break;
12421             }
12422         }
12423     },
12424    
12425 /**
12426  * Executes the specified function once for every key in the collection, passing each
12427  * key, and its associated item as the first two parameters.
12428  * @param {Function} fn The function to execute for each item.
12429  * @param {Object} scope (optional) The scope in which to execute the function.
12430  */
12431     eachKey : function(fn, scope){
12432         for(var i = 0, len = this.keys.length; i < len; i++){
12433             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12434         }
12435     },
12436    
12437 /**
12438  * Returns the first item in the collection which elicits a true return value from the
12439  * passed selection function.
12440  * @param {Function} fn The selection function to execute for each item.
12441  * @param {Object} scope (optional) The scope in which to execute the function.
12442  * @return {Object} The first item in the collection which returned true from the selection function.
12443  */
12444     find : function(fn, scope){
12445         for(var i = 0, len = this.items.length; i < len; i++){
12446             if(fn.call(scope || window, this.items[i], this.keys[i])){
12447                 return this.items[i];
12448             }
12449         }
12450         return null;
12451     },
12452    
12453 /**
12454  * Inserts an item at the specified index in the collection.
12455  * @param {Number} index The index to insert the item at.
12456  * @param {String} key The key to associate with the new item, or the item itself.
12457  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12458  * @return {Object} The item inserted.
12459  */
12460     insert : function(index, key, o){
12461         if(arguments.length == 2){
12462             o = arguments[1];
12463             key = this.getKey(o);
12464         }
12465         if(index >= this.length){
12466             return this.add(key, o);
12467         }
12468         this.length++;
12469         this.items.splice(index, 0, o);
12470         if(typeof key != "undefined" && key != null){
12471             this.map[key] = o;
12472         }
12473         this.keys.splice(index, 0, key);
12474         this.fireEvent("add", index, o, key);
12475         return o;
12476     },
12477    
12478 /**
12479  * Removed an item from the collection.
12480  * @param {Object} o The item to remove.
12481  * @return {Object} The item removed.
12482  */
12483     remove : function(o){
12484         return this.removeAt(this.indexOf(o));
12485     },
12486    
12487 /**
12488  * Remove an item from a specified index in the collection.
12489  * @param {Number} index The index within the collection of the item to remove.
12490  */
12491     removeAt : function(index){
12492         if(index < this.length && index >= 0){
12493             this.length--;
12494             var o = this.items[index];
12495             this.items.splice(index, 1);
12496             var key = this.keys[index];
12497             if(typeof key != "undefined"){
12498                 delete this.map[key];
12499             }
12500             this.keys.splice(index, 1);
12501             this.fireEvent("remove", o, key);
12502         }
12503     },
12504    
12505 /**
12506  * Removed an item associated with the passed key fom the collection.
12507  * @param {String} key The key of the item to remove.
12508  */
12509     removeKey : function(key){
12510         return this.removeAt(this.indexOfKey(key));
12511     },
12512    
12513 /**
12514  * Returns the number of items in the collection.
12515  * @return {Number} the number of items in the collection.
12516  */
12517     getCount : function(){
12518         return this.length; 
12519     },
12520    
12521 /**
12522  * Returns index within the collection of the passed Object.
12523  * @param {Object} o The item to find the index of.
12524  * @return {Number} index of the item.
12525  */
12526     indexOf : function(o){
12527         if(!this.items.indexOf){
12528             for(var i = 0, len = this.items.length; i < len; i++){
12529                 if(this.items[i] == o) return i;
12530             }
12531             return -1;
12532         }else{
12533             return this.items.indexOf(o);
12534         }
12535     },
12536    
12537 /**
12538  * Returns index within the collection of the passed key.
12539  * @param {String} key The key to find the index of.
12540  * @return {Number} index of the key.
12541  */
12542     indexOfKey : function(key){
12543         if(!this.keys.indexOf){
12544             for(var i = 0, len = this.keys.length; i < len; i++){
12545                 if(this.keys[i] == key) return i;
12546             }
12547             return -1;
12548         }else{
12549             return this.keys.indexOf(key);
12550         }
12551     },
12552    
12553 /**
12554  * Returns the item associated with the passed key OR index. Key has priority over index.
12555  * @param {String/Number} key The key or index of the item.
12556  * @return {Object} The item associated with the passed key.
12557  */
12558     item : function(key){
12559         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12560         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12561     },
12562     
12563 /**
12564  * Returns the item at the specified index.
12565  * @param {Number} index The index of the item.
12566  * @return {Object}
12567  */
12568     itemAt : function(index){
12569         return this.items[index];
12570     },
12571     
12572 /**
12573  * Returns the item associated with the passed key.
12574  * @param {String/Number} key The key of the item.
12575  * @return {Object} The item associated with the passed key.
12576  */
12577     key : function(key){
12578         return this.map[key];
12579     },
12580    
12581 /**
12582  * Returns true if the collection contains the passed Object as an item.
12583  * @param {Object} o  The Object to look for in the collection.
12584  * @return {Boolean} True if the collection contains the Object as an item.
12585  */
12586     contains : function(o){
12587         return this.indexOf(o) != -1;
12588     },
12589    
12590 /**
12591  * Returns true if the collection contains the passed Object as a key.
12592  * @param {String} key The key to look for in the collection.
12593  * @return {Boolean} True if the collection contains the Object as a key.
12594  */
12595     containsKey : function(key){
12596         return typeof this.map[key] != "undefined";
12597     },
12598    
12599 /**
12600  * Removes all items from the collection.
12601  */
12602     clear : function(){
12603         this.length = 0;
12604         this.items = [];
12605         this.keys = [];
12606         this.map = {};
12607         this.fireEvent("clear");
12608     },
12609    
12610 /**
12611  * Returns the first item in the collection.
12612  * @return {Object} the first item in the collection..
12613  */
12614     first : function(){
12615         return this.items[0]; 
12616     },
12617    
12618 /**
12619  * Returns the last item in the collection.
12620  * @return {Object} the last item in the collection..
12621  */
12622     last : function(){
12623         return this.items[this.length-1];   
12624     },
12625     
12626     _sort : function(property, dir, fn){
12627         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12628         fn = fn || function(a, b){
12629             return a-b;
12630         };
12631         var c = [], k = this.keys, items = this.items;
12632         for(var i = 0, len = items.length; i < len; i++){
12633             c[c.length] = {key: k[i], value: items[i], index: i};
12634         }
12635         c.sort(function(a, b){
12636             var v = fn(a[property], b[property]) * dsc;
12637             if(v == 0){
12638                 v = (a.index < b.index ? -1 : 1);
12639             }
12640             return v;
12641         });
12642         for(var i = 0, len = c.length; i < len; i++){
12643             items[i] = c[i].value;
12644             k[i] = c[i].key;
12645         }
12646         this.fireEvent("sort", this);
12647     },
12648     
12649     /**
12650      * Sorts this collection with the passed comparison function
12651      * @param {String} direction (optional) "ASC" or "DESC"
12652      * @param {Function} fn (optional) comparison function
12653      */
12654     sort : function(dir, fn){
12655         this._sort("value", dir, fn);
12656     },
12657     
12658     /**
12659      * Sorts this collection by keys
12660      * @param {String} direction (optional) "ASC" or "DESC"
12661      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12662      */
12663     keySort : function(dir, fn){
12664         this._sort("key", dir, fn || function(a, b){
12665             return String(a).toUpperCase()-String(b).toUpperCase();
12666         });
12667     },
12668     
12669     /**
12670      * Returns a range of items in this collection
12671      * @param {Number} startIndex (optional) defaults to 0
12672      * @param {Number} endIndex (optional) default to the last item
12673      * @return {Array} An array of items
12674      */
12675     getRange : function(start, end){
12676         var items = this.items;
12677         if(items.length < 1){
12678             return [];
12679         }
12680         start = start || 0;
12681         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12682         var r = [];
12683         if(start <= end){
12684             for(var i = start; i <= end; i++) {
12685                     r[r.length] = items[i];
12686             }
12687         }else{
12688             for(var i = start; i >= end; i--) {
12689                     r[r.length] = items[i];
12690             }
12691         }
12692         return r;
12693     },
12694         
12695     /**
12696      * Filter the <i>objects</i> in this collection by a specific property. 
12697      * Returns a new collection that has been filtered.
12698      * @param {String} property A property on your objects
12699      * @param {String/RegExp} value Either string that the property values 
12700      * should start with or a RegExp to test against the property
12701      * @return {MixedCollection} The new filtered collection
12702      */
12703     filter : function(property, value){
12704         if(!value.exec){ // not a regex
12705             value = String(value);
12706             if(value.length == 0){
12707                 return this.clone();
12708             }
12709             value = new RegExp("^" + Roo.escapeRe(value), "i");
12710         }
12711         return this.filterBy(function(o){
12712             return o && value.test(o[property]);
12713         });
12714         },
12715     
12716     /**
12717      * Filter by a function. * Returns a new collection that has been filtered.
12718      * The passed function will be called with each 
12719      * object in the collection. If the function returns true, the value is included 
12720      * otherwise it is filtered.
12721      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12722      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12723      * @return {MixedCollection} The new filtered collection
12724      */
12725     filterBy : function(fn, scope){
12726         var r = new Roo.util.MixedCollection();
12727         r.getKey = this.getKey;
12728         var k = this.keys, it = this.items;
12729         for(var i = 0, len = it.length; i < len; i++){
12730             if(fn.call(scope||this, it[i], k[i])){
12731                                 r.add(k[i], it[i]);
12732                         }
12733         }
12734         return r;
12735     },
12736     
12737     /**
12738      * Creates a duplicate of this collection
12739      * @return {MixedCollection}
12740      */
12741     clone : function(){
12742         var r = new Roo.util.MixedCollection();
12743         var k = this.keys, it = this.items;
12744         for(var i = 0, len = it.length; i < len; i++){
12745             r.add(k[i], it[i]);
12746         }
12747         r.getKey = this.getKey;
12748         return r;
12749     }
12750 });
12751 /**
12752  * Returns the item associated with the passed key or index.
12753  * @method
12754  * @param {String/Number} key The key or index of the item.
12755  * @return {Object} The item associated with the passed key.
12756  */
12757 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12758  * Based on:
12759  * Ext JS Library 1.1.1
12760  * Copyright(c) 2006-2007, Ext JS, LLC.
12761  *
12762  * Originally Released Under LGPL - original licence link has changed is not relivant.
12763  *
12764  * Fork - LGPL
12765  * <script type="text/javascript">
12766  */
12767 /**
12768  * @class Roo.util.JSON
12769  * Modified version of Douglas Crockford"s json.js that doesn"t
12770  * mess with the Object prototype 
12771  * http://www.json.org/js.html
12772  * @singleton
12773  */
12774 Roo.util.JSON = new (function(){
12775     var useHasOwn = {}.hasOwnProperty ? true : false;
12776     
12777     // crashes Safari in some instances
12778     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12779     
12780     var pad = function(n) {
12781         return n < 10 ? "0" + n : n;
12782     };
12783     
12784     var m = {
12785         "\b": '\\b',
12786         "\t": '\\t',
12787         "\n": '\\n',
12788         "\f": '\\f',
12789         "\r": '\\r',
12790         '"' : '\\"',
12791         "\\": '\\\\'
12792     };
12793
12794     var encodeString = function(s){
12795         if (/["\\\x00-\x1f]/.test(s)) {
12796             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12797                 var c = m[b];
12798                 if(c){
12799                     return c;
12800                 }
12801                 c = b.charCodeAt();
12802                 return "\\u00" +
12803                     Math.floor(c / 16).toString(16) +
12804                     (c % 16).toString(16);
12805             }) + '"';
12806         }
12807         return '"' + s + '"';
12808     };
12809     
12810     var encodeArray = function(o){
12811         var a = ["["], b, i, l = o.length, v;
12812             for (i = 0; i < l; i += 1) {
12813                 v = o[i];
12814                 switch (typeof v) {
12815                     case "undefined":
12816                     case "function":
12817                     case "unknown":
12818                         break;
12819                     default:
12820                         if (b) {
12821                             a.push(',');
12822                         }
12823                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12824                         b = true;
12825                 }
12826             }
12827             a.push("]");
12828             return a.join("");
12829     };
12830     
12831     var encodeDate = function(o){
12832         return '"' + o.getFullYear() + "-" +
12833                 pad(o.getMonth() + 1) + "-" +
12834                 pad(o.getDate()) + "T" +
12835                 pad(o.getHours()) + ":" +
12836                 pad(o.getMinutes()) + ":" +
12837                 pad(o.getSeconds()) + '"';
12838     };
12839     
12840     /**
12841      * Encodes an Object, Array or other value
12842      * @param {Mixed} o The variable to encode
12843      * @return {String} The JSON string
12844      */
12845     this.encode = function(o)
12846     {
12847         // should this be extended to fully wrap stringify..
12848         
12849         if(typeof o == "undefined" || o === null){
12850             return "null";
12851         }else if(o instanceof Array){
12852             return encodeArray(o);
12853         }else if(o instanceof Date){
12854             return encodeDate(o);
12855         }else if(typeof o == "string"){
12856             return encodeString(o);
12857         }else if(typeof o == "number"){
12858             return isFinite(o) ? String(o) : "null";
12859         }else if(typeof o == "boolean"){
12860             return String(o);
12861         }else {
12862             var a = ["{"], b, i, v;
12863             for (i in o) {
12864                 if(!useHasOwn || o.hasOwnProperty(i)) {
12865                     v = o[i];
12866                     switch (typeof v) {
12867                     case "undefined":
12868                     case "function":
12869                     case "unknown":
12870                         break;
12871                     default:
12872                         if(b){
12873                             a.push(',');
12874                         }
12875                         a.push(this.encode(i), ":",
12876                                 v === null ? "null" : this.encode(v));
12877                         b = true;
12878                     }
12879                 }
12880             }
12881             a.push("}");
12882             return a.join("");
12883         }
12884     };
12885     
12886     /**
12887      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12888      * @param {String} json The JSON string
12889      * @return {Object} The resulting object
12890      */
12891     this.decode = function(json){
12892         
12893         return  /** eval:var:json */ eval("(" + json + ')');
12894     };
12895 })();
12896 /** 
12897  * Shorthand for {@link Roo.util.JSON#encode}
12898  * @member Roo encode 
12899  * @method */
12900 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12901 /** 
12902  * Shorthand for {@link Roo.util.JSON#decode}
12903  * @member Roo decode 
12904  * @method */
12905 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12906 /*
12907  * Based on:
12908  * Ext JS Library 1.1.1
12909  * Copyright(c) 2006-2007, Ext JS, LLC.
12910  *
12911  * Originally Released Under LGPL - original licence link has changed is not relivant.
12912  *
12913  * Fork - LGPL
12914  * <script type="text/javascript">
12915  */
12916  
12917 /**
12918  * @class Roo.util.Format
12919  * Reusable data formatting functions
12920  * @singleton
12921  */
12922 Roo.util.Format = function(){
12923     var trimRe = /^\s+|\s+$/g;
12924     return {
12925         /**
12926          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12927          * @param {String} value The string to truncate
12928          * @param {Number} length The maximum length to allow before truncating
12929          * @return {String} The converted text
12930          */
12931         ellipsis : function(value, len){
12932             if(value && value.length > len){
12933                 return value.substr(0, len-3)+"...";
12934             }
12935             return value;
12936         },
12937
12938         /**
12939          * Checks a reference and converts it to empty string if it is undefined
12940          * @param {Mixed} value Reference to check
12941          * @return {Mixed} Empty string if converted, otherwise the original value
12942          */
12943         undef : function(value){
12944             return typeof value != "undefined" ? value : "";
12945         },
12946
12947         /**
12948          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12949          * @param {String} value The string to encode
12950          * @return {String} The encoded text
12951          */
12952         htmlEncode : function(value){
12953             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12954         },
12955
12956         /**
12957          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12958          * @param {String} value The string to decode
12959          * @return {String} The decoded text
12960          */
12961         htmlDecode : function(value){
12962             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12963         },
12964
12965         /**
12966          * Trims any whitespace from either side of a string
12967          * @param {String} value The text to trim
12968          * @return {String} The trimmed text
12969          */
12970         trim : function(value){
12971             return String(value).replace(trimRe, "");
12972         },
12973
12974         /**
12975          * Returns a substring from within an original string
12976          * @param {String} value The original text
12977          * @param {Number} start The start index of the substring
12978          * @param {Number} length The length of the substring
12979          * @return {String} The substring
12980          */
12981         substr : function(value, start, length){
12982             return String(value).substr(start, length);
12983         },
12984
12985         /**
12986          * Converts a string to all lower case letters
12987          * @param {String} value The text to convert
12988          * @return {String} The converted text
12989          */
12990         lowercase : function(value){
12991             return String(value).toLowerCase();
12992         },
12993
12994         /**
12995          * Converts a string to all upper case letters
12996          * @param {String} value The text to convert
12997          * @return {String} The converted text
12998          */
12999         uppercase : function(value){
13000             return String(value).toUpperCase();
13001         },
13002
13003         /**
13004          * Converts the first character only of a string to upper case
13005          * @param {String} value The text to convert
13006          * @return {String} The converted text
13007          */
13008         capitalize : function(value){
13009             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13010         },
13011
13012         // private
13013         call : function(value, fn){
13014             if(arguments.length > 2){
13015                 var args = Array.prototype.slice.call(arguments, 2);
13016                 args.unshift(value);
13017                  
13018                 return /** eval:var:value */  eval(fn).apply(window, args);
13019             }else{
13020                 /** eval:var:value */
13021                 return /** eval:var:value */ eval(fn).call(window, value);
13022             }
13023         },
13024
13025        
13026         /**
13027          * safer version of Math.toFixed..??/
13028          * @param {Number/String} value The numeric value to format
13029          * @param {Number/String} value Decimal places 
13030          * @return {String} The formatted currency string
13031          */
13032         toFixed : function(v, n)
13033         {
13034             // why not use to fixed - precision is buggered???
13035             if (!n) {
13036                 return Math.round(v-0);
13037             }
13038             var fact = Math.pow(10,n+1);
13039             v = (Math.round((v-0)*fact))/fact;
13040             var z = (''+fact).substring(2);
13041             if (v == Math.floor(v)) {
13042                 return Math.floor(v) + '.' + z;
13043             }
13044             
13045             // now just padd decimals..
13046             var ps = String(v).split('.');
13047             var fd = (ps[1] + z);
13048             var r = fd.substring(0,n); 
13049             var rm = fd.substring(n); 
13050             if (rm < 5) {
13051                 return ps[0] + '.' + r;
13052             }
13053             r*=1; // turn it into a number;
13054             r++;
13055             if (String(r).length != n) {
13056                 ps[0]*=1;
13057                 ps[0]++;
13058                 r = String(r).substring(1); // chop the end off.
13059             }
13060             
13061             return ps[0] + '.' + r;
13062              
13063         },
13064         
13065         /**
13066          * Format a number as US currency
13067          * @param {Number/String} value The numeric value to format
13068          * @return {String} The formatted currency string
13069          */
13070         usMoney : function(v){
13071             v = (Math.round((v-0)*100))/100;
13072             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13073             v = String(v);
13074             var ps = v.split('.');
13075             var whole = ps[0];
13076             var sub = ps[1] ? '.'+ ps[1] : '.00';
13077             var r = /(\d+)(\d{3})/;
13078             while (r.test(whole)) {
13079                 whole = whole.replace(r, '$1' + ',' + '$2');
13080             }
13081             return "$" + whole + sub ;
13082         },
13083         
13084         /**
13085          * Parse a value into a formatted date using the specified format pattern.
13086          * @param {Mixed} value The value to format
13087          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13088          * @return {String} The formatted date string
13089          */
13090         date : function(v, format){
13091             if(!v){
13092                 return "";
13093             }
13094             if(!(v instanceof Date)){
13095                 v = new Date(Date.parse(v));
13096             }
13097             return v.dateFormat(format || "m/d/Y");
13098         },
13099
13100         /**
13101          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13102          * @param {String} format Any valid date format string
13103          * @return {Function} The date formatting function
13104          */
13105         dateRenderer : function(format){
13106             return function(v){
13107                 return Roo.util.Format.date(v, format);  
13108             };
13109         },
13110
13111         // private
13112         stripTagsRE : /<\/?[^>]+>/gi,
13113         
13114         /**
13115          * Strips all HTML tags
13116          * @param {Mixed} value The text from which to strip tags
13117          * @return {String} The stripped text
13118          */
13119         stripTags : function(v){
13120             return !v ? v : String(v).replace(this.stripTagsRE, "");
13121         }
13122     };
13123 }();/*
13124  * Based on:
13125  * Ext JS Library 1.1.1
13126  * Copyright(c) 2006-2007, Ext JS, LLC.
13127  *
13128  * Originally Released Under LGPL - original licence link has changed is not relivant.
13129  *
13130  * Fork - LGPL
13131  * <script type="text/javascript">
13132  */
13133
13134
13135  
13136
13137 /**
13138  * @class Roo.MasterTemplate
13139  * @extends Roo.Template
13140  * Provides a template that can have child templates. The syntax is:
13141 <pre><code>
13142 var t = new Roo.MasterTemplate(
13143         '&lt;select name="{name}"&gt;',
13144                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13145         '&lt;/select&gt;'
13146 );
13147 t.add('options', {value: 'foo', text: 'bar'});
13148 // or you can add multiple child elements in one shot
13149 t.addAll('options', [
13150     {value: 'foo', text: 'bar'},
13151     {value: 'foo2', text: 'bar2'},
13152     {value: 'foo3', text: 'bar3'}
13153 ]);
13154 // then append, applying the master template values
13155 t.append('my-form', {name: 'my-select'});
13156 </code></pre>
13157 * A name attribute for the child template is not required if you have only one child
13158 * template or you want to refer to them by index.
13159  */
13160 Roo.MasterTemplate = function(){
13161     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13162     this.originalHtml = this.html;
13163     var st = {};
13164     var m, re = this.subTemplateRe;
13165     re.lastIndex = 0;
13166     var subIndex = 0;
13167     while(m = re.exec(this.html)){
13168         var name = m[1], content = m[2];
13169         st[subIndex] = {
13170             name: name,
13171             index: subIndex,
13172             buffer: [],
13173             tpl : new Roo.Template(content)
13174         };
13175         if(name){
13176             st[name] = st[subIndex];
13177         }
13178         st[subIndex].tpl.compile();
13179         st[subIndex].tpl.call = this.call.createDelegate(this);
13180         subIndex++;
13181     }
13182     this.subCount = subIndex;
13183     this.subs = st;
13184 };
13185 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13186     /**
13187     * The regular expression used to match sub templates
13188     * @type RegExp
13189     * @property
13190     */
13191     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13192
13193     /**
13194      * Applies the passed values to a child template.
13195      * @param {String/Number} name (optional) The name or index of the child template
13196      * @param {Array/Object} values The values to be applied to the template
13197      * @return {MasterTemplate} this
13198      */
13199      add : function(name, values){
13200         if(arguments.length == 1){
13201             values = arguments[0];
13202             name = 0;
13203         }
13204         var s = this.subs[name];
13205         s.buffer[s.buffer.length] = s.tpl.apply(values);
13206         return this;
13207     },
13208
13209     /**
13210      * Applies all the passed values to a child template.
13211      * @param {String/Number} name (optional) The name or index of the child template
13212      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13213      * @param {Boolean} reset (optional) True to reset the template first
13214      * @return {MasterTemplate} this
13215      */
13216     fill : function(name, values, reset){
13217         var a = arguments;
13218         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13219             values = a[0];
13220             name = 0;
13221             reset = a[1];
13222         }
13223         if(reset){
13224             this.reset();
13225         }
13226         for(var i = 0, len = values.length; i < len; i++){
13227             this.add(name, values[i]);
13228         }
13229         return this;
13230     },
13231
13232     /**
13233      * Resets the template for reuse
13234      * @return {MasterTemplate} this
13235      */
13236      reset : function(){
13237         var s = this.subs;
13238         for(var i = 0; i < this.subCount; i++){
13239             s[i].buffer = [];
13240         }
13241         return this;
13242     },
13243
13244     applyTemplate : function(values){
13245         var s = this.subs;
13246         var replaceIndex = -1;
13247         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13248             return s[++replaceIndex].buffer.join("");
13249         });
13250         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13251     },
13252
13253     apply : function(){
13254         return this.applyTemplate.apply(this, arguments);
13255     },
13256
13257     compile : function(){return this;}
13258 });
13259
13260 /**
13261  * Alias for fill().
13262  * @method
13263  */
13264 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13265  /**
13266  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13267  * var tpl = Roo.MasterTemplate.from('element-id');
13268  * @param {String/HTMLElement} el
13269  * @param {Object} config
13270  * @static
13271  */
13272 Roo.MasterTemplate.from = function(el, config){
13273     el = Roo.getDom(el);
13274     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13275 };/*
13276  * Based on:
13277  * Ext JS Library 1.1.1
13278  * Copyright(c) 2006-2007, Ext JS, LLC.
13279  *
13280  * Originally Released Under LGPL - original licence link has changed is not relivant.
13281  *
13282  * Fork - LGPL
13283  * <script type="text/javascript">
13284  */
13285
13286  
13287 /**
13288  * @class Roo.util.CSS
13289  * Utility class for manipulating CSS rules
13290  * @singleton
13291  */
13292 Roo.util.CSS = function(){
13293         var rules = null;
13294         var doc = document;
13295
13296     var camelRe = /(-[a-z])/gi;
13297     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13298
13299    return {
13300    /**
13301     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13302     * tag and appended to the HEAD of the document.
13303     * @param {String|Object} cssText The text containing the css rules
13304     * @param {String} id An id to add to the stylesheet for later removal
13305     * @return {StyleSheet}
13306     */
13307     createStyleSheet : function(cssText, id){
13308         var ss;
13309         var head = doc.getElementsByTagName("head")[0];
13310         var nrules = doc.createElement("style");
13311         nrules.setAttribute("type", "text/css");
13312         if(id){
13313             nrules.setAttribute("id", id);
13314         }
13315         if (typeof(cssText) != 'string') {
13316             // support object maps..
13317             // not sure if this a good idea.. 
13318             // perhaps it should be merged with the general css handling
13319             // and handle js style props.
13320             var cssTextNew = [];
13321             for(var n in cssText) {
13322                 var citems = [];
13323                 for(var k in cssText[n]) {
13324                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13325                 }
13326                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13327                 
13328             }
13329             cssText = cssTextNew.join("\n");
13330             
13331         }
13332        
13333        
13334        if(Roo.isIE){
13335            head.appendChild(nrules);
13336            ss = nrules.styleSheet;
13337            ss.cssText = cssText;
13338        }else{
13339            try{
13340                 nrules.appendChild(doc.createTextNode(cssText));
13341            }catch(e){
13342                nrules.cssText = cssText; 
13343            }
13344            head.appendChild(nrules);
13345            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13346        }
13347        this.cacheStyleSheet(ss);
13348        return ss;
13349    },
13350
13351    /**
13352     * Removes a style or link tag by id
13353     * @param {String} id The id of the tag
13354     */
13355    removeStyleSheet : function(id){
13356        var existing = doc.getElementById(id);
13357        if(existing){
13358            existing.parentNode.removeChild(existing);
13359        }
13360    },
13361
13362    /**
13363     * Dynamically swaps an existing stylesheet reference for a new one
13364     * @param {String} id The id of an existing link tag to remove
13365     * @param {String} url The href of the new stylesheet to include
13366     */
13367    swapStyleSheet : function(id, url){
13368        this.removeStyleSheet(id);
13369        var ss = doc.createElement("link");
13370        ss.setAttribute("rel", "stylesheet");
13371        ss.setAttribute("type", "text/css");
13372        ss.setAttribute("id", id);
13373        ss.setAttribute("href", url);
13374        doc.getElementsByTagName("head")[0].appendChild(ss);
13375    },
13376    
13377    /**
13378     * Refresh the rule cache if you have dynamically added stylesheets
13379     * @return {Object} An object (hash) of rules indexed by selector
13380     */
13381    refreshCache : function(){
13382        return this.getRules(true);
13383    },
13384
13385    // private
13386    cacheStyleSheet : function(stylesheet){
13387        if(!rules){
13388            rules = {};
13389        }
13390        try{// try catch for cross domain access issue
13391            var ssRules = stylesheet.cssRules || stylesheet.rules;
13392            for(var j = ssRules.length-1; j >= 0; --j){
13393                rules[ssRules[j].selectorText] = ssRules[j];
13394            }
13395        }catch(e){}
13396    },
13397    
13398    /**
13399     * Gets all css rules for the document
13400     * @param {Boolean} refreshCache true to refresh the internal cache
13401     * @return {Object} An object (hash) of rules indexed by selector
13402     */
13403    getRules : function(refreshCache){
13404                 if(rules == null || refreshCache){
13405                         rules = {};
13406                         var ds = doc.styleSheets;
13407                         for(var i =0, len = ds.length; i < len; i++){
13408                             try{
13409                         this.cacheStyleSheet(ds[i]);
13410                     }catch(e){} 
13411                 }
13412                 }
13413                 return rules;
13414         },
13415         
13416         /**
13417     * Gets an an individual CSS rule by selector(s)
13418     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13419     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13420     * @return {CSSRule} The CSS rule or null if one is not found
13421     */
13422    getRule : function(selector, refreshCache){
13423                 var rs = this.getRules(refreshCache);
13424                 if(!(selector instanceof Array)){
13425                     return rs[selector];
13426                 }
13427                 for(var i = 0; i < selector.length; i++){
13428                         if(rs[selector[i]]){
13429                                 return rs[selector[i]];
13430                         }
13431                 }
13432                 return null;
13433         },
13434         
13435         
13436         /**
13437     * Updates a rule property
13438     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13439     * @param {String} property The css property
13440     * @param {String} value The new value for the property
13441     * @return {Boolean} true If a rule was found and updated
13442     */
13443    updateRule : function(selector, property, value){
13444                 if(!(selector instanceof Array)){
13445                         var rule = this.getRule(selector);
13446                         if(rule){
13447                                 rule.style[property.replace(camelRe, camelFn)] = value;
13448                                 return true;
13449                         }
13450                 }else{
13451                         for(var i = 0; i < selector.length; i++){
13452                                 if(this.updateRule(selector[i], property, value)){
13453                                         return true;
13454                                 }
13455                         }
13456                 }
13457                 return false;
13458         }
13459    };   
13460 }();/*
13461  * Based on:
13462  * Ext JS Library 1.1.1
13463  * Copyright(c) 2006-2007, Ext JS, LLC.
13464  *
13465  * Originally Released Under LGPL - original licence link has changed is not relivant.
13466  *
13467  * Fork - LGPL
13468  * <script type="text/javascript">
13469  */
13470
13471  
13472
13473 /**
13474  * @class Roo.util.ClickRepeater
13475  * @extends Roo.util.Observable
13476  * 
13477  * A wrapper class which can be applied to any element. Fires a "click" event while the
13478  * mouse is pressed. The interval between firings may be specified in the config but
13479  * defaults to 10 milliseconds.
13480  * 
13481  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13482  * 
13483  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13484  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13485  * Similar to an autorepeat key delay.
13486  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13487  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13488  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13489  *           "interval" and "delay" are ignored. "immediate" is honored.
13490  * @cfg {Boolean} preventDefault True to prevent the default click event
13491  * @cfg {Boolean} stopDefault True to stop the default click event
13492  * 
13493  * @history
13494  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13495  *     2007-02-02 jvs Renamed to ClickRepeater
13496  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13497  *
13498  *  @constructor
13499  * @param {String/HTMLElement/Element} el The element to listen on
13500  * @param {Object} config
13501  **/
13502 Roo.util.ClickRepeater = function(el, config)
13503 {
13504     this.el = Roo.get(el);
13505     this.el.unselectable();
13506
13507     Roo.apply(this, config);
13508
13509     this.addEvents({
13510     /**
13511      * @event mousedown
13512      * Fires when the mouse button is depressed.
13513      * @param {Roo.util.ClickRepeater} this
13514      */
13515         "mousedown" : true,
13516     /**
13517      * @event click
13518      * Fires on a specified interval during the time the element is pressed.
13519      * @param {Roo.util.ClickRepeater} this
13520      */
13521         "click" : true,
13522     /**
13523      * @event mouseup
13524      * Fires when the mouse key is released.
13525      * @param {Roo.util.ClickRepeater} this
13526      */
13527         "mouseup" : true
13528     });
13529
13530     this.el.on("mousedown", this.handleMouseDown, this);
13531     if(this.preventDefault || this.stopDefault){
13532         this.el.on("click", function(e){
13533             if(this.preventDefault){
13534                 e.preventDefault();
13535             }
13536             if(this.stopDefault){
13537                 e.stopEvent();
13538             }
13539         }, this);
13540     }
13541
13542     // allow inline handler
13543     if(this.handler){
13544         this.on("click", this.handler,  this.scope || this);
13545     }
13546
13547     Roo.util.ClickRepeater.superclass.constructor.call(this);
13548 };
13549
13550 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13551     interval : 20,
13552     delay: 250,
13553     preventDefault : true,
13554     stopDefault : false,
13555     timer : 0,
13556
13557     // private
13558     handleMouseDown : function(){
13559         clearTimeout(this.timer);
13560         this.el.blur();
13561         if(this.pressClass){
13562             this.el.addClass(this.pressClass);
13563         }
13564         this.mousedownTime = new Date();
13565
13566         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13567         this.el.on("mouseout", this.handleMouseOut, this);
13568
13569         this.fireEvent("mousedown", this);
13570         this.fireEvent("click", this);
13571         
13572         this.timer = this.click.defer(this.delay || this.interval, this);
13573     },
13574
13575     // private
13576     click : function(){
13577         this.fireEvent("click", this);
13578         this.timer = this.click.defer(this.getInterval(), this);
13579     },
13580
13581     // private
13582     getInterval: function(){
13583         if(!this.accelerate){
13584             return this.interval;
13585         }
13586         var pressTime = this.mousedownTime.getElapsed();
13587         if(pressTime < 500){
13588             return 400;
13589         }else if(pressTime < 1700){
13590             return 320;
13591         }else if(pressTime < 2600){
13592             return 250;
13593         }else if(pressTime < 3500){
13594             return 180;
13595         }else if(pressTime < 4400){
13596             return 140;
13597         }else if(pressTime < 5300){
13598             return 80;
13599         }else if(pressTime < 6200){
13600             return 50;
13601         }else{
13602             return 10;
13603         }
13604     },
13605
13606     // private
13607     handleMouseOut : function(){
13608         clearTimeout(this.timer);
13609         if(this.pressClass){
13610             this.el.removeClass(this.pressClass);
13611         }
13612         this.el.on("mouseover", this.handleMouseReturn, this);
13613     },
13614
13615     // private
13616     handleMouseReturn : function(){
13617         this.el.un("mouseover", this.handleMouseReturn);
13618         if(this.pressClass){
13619             this.el.addClass(this.pressClass);
13620         }
13621         this.click();
13622     },
13623
13624     // private
13625     handleMouseUp : function(){
13626         clearTimeout(this.timer);
13627         this.el.un("mouseover", this.handleMouseReturn);
13628         this.el.un("mouseout", this.handleMouseOut);
13629         Roo.get(document).un("mouseup", this.handleMouseUp);
13630         this.el.removeClass(this.pressClass);
13631         this.fireEvent("mouseup", this);
13632     }
13633 });/*
13634  * Based on:
13635  * Ext JS Library 1.1.1
13636  * Copyright(c) 2006-2007, Ext JS, LLC.
13637  *
13638  * Originally Released Under LGPL - original licence link has changed is not relivant.
13639  *
13640  * Fork - LGPL
13641  * <script type="text/javascript">
13642  */
13643
13644  
13645 /**
13646  * @class Roo.KeyNav
13647  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13648  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13649  * way to implement custom navigation schemes for any UI component.</p>
13650  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13651  * pageUp, pageDown, del, home, end.  Usage:</p>
13652  <pre><code>
13653 var nav = new Roo.KeyNav("my-element", {
13654     "left" : function(e){
13655         this.moveLeft(e.ctrlKey);
13656     },
13657     "right" : function(e){
13658         this.moveRight(e.ctrlKey);
13659     },
13660     "enter" : function(e){
13661         this.save();
13662     },
13663     scope : this
13664 });
13665 </code></pre>
13666  * @constructor
13667  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13668  * @param {Object} config The config
13669  */
13670 Roo.KeyNav = function(el, config){
13671     this.el = Roo.get(el);
13672     Roo.apply(this, config);
13673     if(!this.disabled){
13674         this.disabled = true;
13675         this.enable();
13676     }
13677 };
13678
13679 Roo.KeyNav.prototype = {
13680     /**
13681      * @cfg {Boolean} disabled
13682      * True to disable this KeyNav instance (defaults to false)
13683      */
13684     disabled : false,
13685     /**
13686      * @cfg {String} defaultEventAction
13687      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13688      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13689      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13690      */
13691     defaultEventAction: "stopEvent",
13692     /**
13693      * @cfg {Boolean} forceKeyDown
13694      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13695      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13696      * handle keydown instead of keypress.
13697      */
13698     forceKeyDown : false,
13699
13700     // private
13701     prepareEvent : function(e){
13702         var k = e.getKey();
13703         var h = this.keyToHandler[k];
13704         //if(h && this[h]){
13705         //    e.stopPropagation();
13706         //}
13707         if(Roo.isSafari && h && k >= 37 && k <= 40){
13708             e.stopEvent();
13709         }
13710     },
13711
13712     // private
13713     relay : function(e){
13714         var k = e.getKey();
13715         var h = this.keyToHandler[k];
13716         if(h && this[h]){
13717             if(this.doRelay(e, this[h], h) !== true){
13718                 e[this.defaultEventAction]();
13719             }
13720         }
13721     },
13722
13723     // private
13724     doRelay : function(e, h, hname){
13725         return h.call(this.scope || this, e);
13726     },
13727
13728     // possible handlers
13729     enter : false,
13730     left : false,
13731     right : false,
13732     up : false,
13733     down : false,
13734     tab : false,
13735     esc : false,
13736     pageUp : false,
13737     pageDown : false,
13738     del : false,
13739     home : false,
13740     end : false,
13741
13742     // quick lookup hash
13743     keyToHandler : {
13744         37 : "left",
13745         39 : "right",
13746         38 : "up",
13747         40 : "down",
13748         33 : "pageUp",
13749         34 : "pageDown",
13750         46 : "del",
13751         36 : "home",
13752         35 : "end",
13753         13 : "enter",
13754         27 : "esc",
13755         9  : "tab"
13756     },
13757
13758         /**
13759          * Enable this KeyNav
13760          */
13761         enable: function(){
13762                 if(this.disabled){
13763             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13764             // the EventObject will normalize Safari automatically
13765             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13766                 this.el.on("keydown", this.relay,  this);
13767             }else{
13768                 this.el.on("keydown", this.prepareEvent,  this);
13769                 this.el.on("keypress", this.relay,  this);
13770             }
13771                     this.disabled = false;
13772                 }
13773         },
13774
13775         /**
13776          * Disable this KeyNav
13777          */
13778         disable: function(){
13779                 if(!this.disabled){
13780                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13781                 this.el.un("keydown", this.relay);
13782             }else{
13783                 this.el.un("keydown", this.prepareEvent);
13784                 this.el.un("keypress", this.relay);
13785             }
13786                     this.disabled = true;
13787                 }
13788         }
13789 };/*
13790  * Based on:
13791  * Ext JS Library 1.1.1
13792  * Copyright(c) 2006-2007, Ext JS, LLC.
13793  *
13794  * Originally Released Under LGPL - original licence link has changed is not relivant.
13795  *
13796  * Fork - LGPL
13797  * <script type="text/javascript">
13798  */
13799
13800  
13801 /**
13802  * @class Roo.KeyMap
13803  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13804  * The constructor accepts the same config object as defined by {@link #addBinding}.
13805  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13806  * combination it will call the function with this signature (if the match is a multi-key
13807  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13808  * A KeyMap can also handle a string representation of keys.<br />
13809  * Usage:
13810  <pre><code>
13811 // map one key by key code
13812 var map = new Roo.KeyMap("my-element", {
13813     key: 13, // or Roo.EventObject.ENTER
13814     fn: myHandler,
13815     scope: myObject
13816 });
13817
13818 // map multiple keys to one action by string
13819 var map = new Roo.KeyMap("my-element", {
13820     key: "a\r\n\t",
13821     fn: myHandler,
13822     scope: myObject
13823 });
13824
13825 // map multiple keys to multiple actions by strings and array of codes
13826 var map = new Roo.KeyMap("my-element", [
13827     {
13828         key: [10,13],
13829         fn: function(){ alert("Return was pressed"); }
13830     }, {
13831         key: "abc",
13832         fn: function(){ alert('a, b or c was pressed'); }
13833     }, {
13834         key: "\t",
13835         ctrl:true,
13836         shift:true,
13837         fn: function(){ alert('Control + shift + tab was pressed.'); }
13838     }
13839 ]);
13840 </code></pre>
13841  * <b>Note: A KeyMap starts enabled</b>
13842  * @constructor
13843  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13844  * @param {Object} config The config (see {@link #addBinding})
13845  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13846  */
13847 Roo.KeyMap = function(el, config, eventName){
13848     this.el  = Roo.get(el);
13849     this.eventName = eventName || "keydown";
13850     this.bindings = [];
13851     if(config){
13852         this.addBinding(config);
13853     }
13854     this.enable();
13855 };
13856
13857 Roo.KeyMap.prototype = {
13858     /**
13859      * True to stop the event from bubbling and prevent the default browser action if the
13860      * key was handled by the KeyMap (defaults to false)
13861      * @type Boolean
13862      */
13863     stopEvent : false,
13864
13865     /**
13866      * Add a new binding to this KeyMap. The following config object properties are supported:
13867      * <pre>
13868 Property    Type             Description
13869 ----------  ---------------  ----------------------------------------------------------------------
13870 key         String/Array     A single keycode or an array of keycodes to handle
13871 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13872 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13873 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13874 fn          Function         The function to call when KeyMap finds the expected key combination
13875 scope       Object           The scope of the callback function
13876 </pre>
13877      *
13878      * Usage:
13879      * <pre><code>
13880 // Create a KeyMap
13881 var map = new Roo.KeyMap(document, {
13882     key: Roo.EventObject.ENTER,
13883     fn: handleKey,
13884     scope: this
13885 });
13886
13887 //Add a new binding to the existing KeyMap later
13888 map.addBinding({
13889     key: 'abc',
13890     shift: true,
13891     fn: handleKey,
13892     scope: this
13893 });
13894 </code></pre>
13895      * @param {Object/Array} config A single KeyMap config or an array of configs
13896      */
13897         addBinding : function(config){
13898         if(config instanceof Array){
13899             for(var i = 0, len = config.length; i < len; i++){
13900                 this.addBinding(config[i]);
13901             }
13902             return;
13903         }
13904         var keyCode = config.key,
13905             shift = config.shift, 
13906             ctrl = config.ctrl, 
13907             alt = config.alt,
13908             fn = config.fn,
13909             scope = config.scope;
13910         if(typeof keyCode == "string"){
13911             var ks = [];
13912             var keyString = keyCode.toUpperCase();
13913             for(var j = 0, len = keyString.length; j < len; j++){
13914                 ks.push(keyString.charCodeAt(j));
13915             }
13916             keyCode = ks;
13917         }
13918         var keyArray = keyCode instanceof Array;
13919         var handler = function(e){
13920             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13921                 var k = e.getKey();
13922                 if(keyArray){
13923                     for(var i = 0, len = keyCode.length; i < len; i++){
13924                         if(keyCode[i] == k){
13925                           if(this.stopEvent){
13926                               e.stopEvent();
13927                           }
13928                           fn.call(scope || window, k, e);
13929                           return;
13930                         }
13931                     }
13932                 }else{
13933                     if(k == keyCode){
13934                         if(this.stopEvent){
13935                            e.stopEvent();
13936                         }
13937                         fn.call(scope || window, k, e);
13938                     }
13939                 }
13940             }
13941         };
13942         this.bindings.push(handler);  
13943         },
13944
13945     /**
13946      * Shorthand for adding a single key listener
13947      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13948      * following options:
13949      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13950      * @param {Function} fn The function to call
13951      * @param {Object} scope (optional) The scope of the function
13952      */
13953     on : function(key, fn, scope){
13954         var keyCode, shift, ctrl, alt;
13955         if(typeof key == "object" && !(key instanceof Array)){
13956             keyCode = key.key;
13957             shift = key.shift;
13958             ctrl = key.ctrl;
13959             alt = key.alt;
13960         }else{
13961             keyCode = key;
13962         }
13963         this.addBinding({
13964             key: keyCode,
13965             shift: shift,
13966             ctrl: ctrl,
13967             alt: alt,
13968             fn: fn,
13969             scope: scope
13970         })
13971     },
13972
13973     // private
13974     handleKeyDown : function(e){
13975             if(this.enabled){ //just in case
13976             var b = this.bindings;
13977             for(var i = 0, len = b.length; i < len; i++){
13978                 b[i].call(this, e);
13979             }
13980             }
13981         },
13982         
13983         /**
13984          * Returns true if this KeyMap is enabled
13985          * @return {Boolean} 
13986          */
13987         isEnabled : function(){
13988             return this.enabled;  
13989         },
13990         
13991         /**
13992          * Enables this KeyMap
13993          */
13994         enable: function(){
13995                 if(!this.enabled){
13996                     this.el.on(this.eventName, this.handleKeyDown, this);
13997                     this.enabled = true;
13998                 }
13999         },
14000
14001         /**
14002          * Disable this KeyMap
14003          */
14004         disable: function(){
14005                 if(this.enabled){
14006                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14007                     this.enabled = false;
14008                 }
14009         }
14010 };/*
14011  * Based on:
14012  * Ext JS Library 1.1.1
14013  * Copyright(c) 2006-2007, Ext JS, LLC.
14014  *
14015  * Originally Released Under LGPL - original licence link has changed is not relivant.
14016  *
14017  * Fork - LGPL
14018  * <script type="text/javascript">
14019  */
14020
14021  
14022 /**
14023  * @class Roo.util.TextMetrics
14024  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14025  * wide, in pixels, a given block of text will be.
14026  * @singleton
14027  */
14028 Roo.util.TextMetrics = function(){
14029     var shared;
14030     return {
14031         /**
14032          * Measures the size of the specified text
14033          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14034          * that can affect the size of the rendered text
14035          * @param {String} text The text to measure
14036          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14037          * in order to accurately measure the text height
14038          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14039          */
14040         measure : function(el, text, fixedWidth){
14041             if(!shared){
14042                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14043             }
14044             shared.bind(el);
14045             shared.setFixedWidth(fixedWidth || 'auto');
14046             return shared.getSize(text);
14047         },
14048
14049         /**
14050          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14051          * the overhead of multiple calls to initialize the style properties on each measurement.
14052          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14053          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14054          * in order to accurately measure the text height
14055          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14056          */
14057         createInstance : function(el, fixedWidth){
14058             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14059         }
14060     };
14061 }();
14062
14063  
14064
14065 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14066     var ml = new Roo.Element(document.createElement('div'));
14067     document.body.appendChild(ml.dom);
14068     ml.position('absolute');
14069     ml.setLeftTop(-1000, -1000);
14070     ml.hide();
14071
14072     if(fixedWidth){
14073         ml.setWidth(fixedWidth);
14074     }
14075      
14076     var instance = {
14077         /**
14078          * Returns the size of the specified text based on the internal element's style and width properties
14079          * @memberOf Roo.util.TextMetrics.Instance#
14080          * @param {String} text The text to measure
14081          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14082          */
14083         getSize : function(text){
14084             ml.update(text);
14085             var s = ml.getSize();
14086             ml.update('');
14087             return s;
14088         },
14089
14090         /**
14091          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14092          * that can affect the size of the rendered text
14093          * @memberOf Roo.util.TextMetrics.Instance#
14094          * @param {String/HTMLElement} el The element, dom node or id
14095          */
14096         bind : function(el){
14097             ml.setStyle(
14098                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14099             );
14100         },
14101
14102         /**
14103          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14104          * to set a fixed width in order to accurately measure the text height.
14105          * @memberOf Roo.util.TextMetrics.Instance#
14106          * @param {Number} width The width to set on the element
14107          */
14108         setFixedWidth : function(width){
14109             ml.setWidth(width);
14110         },
14111
14112         /**
14113          * Returns the measured width of the specified text
14114          * @memberOf Roo.util.TextMetrics.Instance#
14115          * @param {String} text The text to measure
14116          * @return {Number} width The width in pixels
14117          */
14118         getWidth : function(text){
14119             ml.dom.style.width = 'auto';
14120             return this.getSize(text).width;
14121         },
14122
14123         /**
14124          * Returns the measured height of the specified text.  For multiline text, be sure to call
14125          * {@link #setFixedWidth} if necessary.
14126          * @memberOf Roo.util.TextMetrics.Instance#
14127          * @param {String} text The text to measure
14128          * @return {Number} height The height in pixels
14129          */
14130         getHeight : function(text){
14131             return this.getSize(text).height;
14132         }
14133     };
14134
14135     instance.bind(bindTo);
14136
14137     return instance;
14138 };
14139
14140 // backwards compat
14141 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14142  * Based on:
14143  * Ext JS Library 1.1.1
14144  * Copyright(c) 2006-2007, Ext JS, LLC.
14145  *
14146  * Originally Released Under LGPL - original licence link has changed is not relivant.
14147  *
14148  * Fork - LGPL
14149  * <script type="text/javascript">
14150  */
14151
14152 /**
14153  * @class Roo.state.Provider
14154  * Abstract base class for state provider implementations. This class provides methods
14155  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14156  * Provider interface.
14157  */
14158 Roo.state.Provider = function(){
14159     /**
14160      * @event statechange
14161      * Fires when a state change occurs.
14162      * @param {Provider} this This state provider
14163      * @param {String} key The state key which was changed
14164      * @param {String} value The encoded value for the state
14165      */
14166     this.addEvents({
14167         "statechange": true
14168     });
14169     this.state = {};
14170     Roo.state.Provider.superclass.constructor.call(this);
14171 };
14172 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14173     /**
14174      * Returns the current value for a key
14175      * @param {String} name The key name
14176      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14177      * @return {Mixed} The state data
14178      */
14179     get : function(name, defaultValue){
14180         return typeof this.state[name] == "undefined" ?
14181             defaultValue : this.state[name];
14182     },
14183     
14184     /**
14185      * Clears a value from the state
14186      * @param {String} name The key name
14187      */
14188     clear : function(name){
14189         delete this.state[name];
14190         this.fireEvent("statechange", this, name, null);
14191     },
14192     
14193     /**
14194      * Sets the value for a key
14195      * @param {String} name The key name
14196      * @param {Mixed} value The value to set
14197      */
14198     set : function(name, value){
14199         this.state[name] = value;
14200         this.fireEvent("statechange", this, name, value);
14201     },
14202     
14203     /**
14204      * Decodes a string previously encoded with {@link #encodeValue}.
14205      * @param {String} value The value to decode
14206      * @return {Mixed} The decoded value
14207      */
14208     decodeValue : function(cookie){
14209         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14210         var matches = re.exec(unescape(cookie));
14211         if(!matches || !matches[1]) return; // non state cookie
14212         var type = matches[1];
14213         var v = matches[2];
14214         switch(type){
14215             case "n":
14216                 return parseFloat(v);
14217             case "d":
14218                 return new Date(Date.parse(v));
14219             case "b":
14220                 return (v == "1");
14221             case "a":
14222                 var all = [];
14223                 var values = v.split("^");
14224                 for(var i = 0, len = values.length; i < len; i++){
14225                     all.push(this.decodeValue(values[i]));
14226                 }
14227                 return all;
14228            case "o":
14229                 var all = {};
14230                 var values = v.split("^");
14231                 for(var i = 0, len = values.length; i < len; i++){
14232                     var kv = values[i].split("=");
14233                     all[kv[0]] = this.decodeValue(kv[1]);
14234                 }
14235                 return all;
14236            default:
14237                 return v;
14238         }
14239     },
14240     
14241     /**
14242      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14243      * @param {Mixed} value The value to encode
14244      * @return {String} The encoded value
14245      */
14246     encodeValue : function(v){
14247         var enc;
14248         if(typeof v == "number"){
14249             enc = "n:" + v;
14250         }else if(typeof v == "boolean"){
14251             enc = "b:" + (v ? "1" : "0");
14252         }else if(v instanceof Date){
14253             enc = "d:" + v.toGMTString();
14254         }else if(v instanceof Array){
14255             var flat = "";
14256             for(var i = 0, len = v.length; i < len; i++){
14257                 flat += this.encodeValue(v[i]);
14258                 if(i != len-1) flat += "^";
14259             }
14260             enc = "a:" + flat;
14261         }else if(typeof v == "object"){
14262             var flat = "";
14263             for(var key in v){
14264                 if(typeof v[key] != "function"){
14265                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14266                 }
14267             }
14268             enc = "o:" + flat.substring(0, flat.length-1);
14269         }else{
14270             enc = "s:" + v;
14271         }
14272         return escape(enc);        
14273     }
14274 });
14275
14276 /*
14277  * Based on:
14278  * Ext JS Library 1.1.1
14279  * Copyright(c) 2006-2007, Ext JS, LLC.
14280  *
14281  * Originally Released Under LGPL - original licence link has changed is not relivant.
14282  *
14283  * Fork - LGPL
14284  * <script type="text/javascript">
14285  */
14286 /**
14287  * @class Roo.state.Manager
14288  * This is the global state manager. By default all components that are "state aware" check this class
14289  * for state information if you don't pass them a custom state provider. In order for this class
14290  * to be useful, it must be initialized with a provider when your application initializes.
14291  <pre><code>
14292 // in your initialization function
14293 init : function(){
14294    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14295    ...
14296    // supposed you have a {@link Roo.BorderLayout}
14297    var layout = new Roo.BorderLayout(...);
14298    layout.restoreState();
14299    // or a {Roo.BasicDialog}
14300    var dialog = new Roo.BasicDialog(...);
14301    dialog.restoreState();
14302  </code></pre>
14303  * @singleton
14304  */
14305 Roo.state.Manager = function(){
14306     var provider = new Roo.state.Provider();
14307     
14308     return {
14309         /**
14310          * Configures the default state provider for your application
14311          * @param {Provider} stateProvider The state provider to set
14312          */
14313         setProvider : function(stateProvider){
14314             provider = stateProvider;
14315         },
14316         
14317         /**
14318          * Returns the current value for a key
14319          * @param {String} name The key name
14320          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14321          * @return {Mixed} The state data
14322          */
14323         get : function(key, defaultValue){
14324             return provider.get(key, defaultValue);
14325         },
14326         
14327         /**
14328          * Sets the value for a key
14329          * @param {String} name The key name
14330          * @param {Mixed} value The state data
14331          */
14332          set : function(key, value){
14333             provider.set(key, value);
14334         },
14335         
14336         /**
14337          * Clears a value from the state
14338          * @param {String} name The key name
14339          */
14340         clear : function(key){
14341             provider.clear(key);
14342         },
14343         
14344         /**
14345          * Gets the currently configured state provider
14346          * @return {Provider} The state provider
14347          */
14348         getProvider : function(){
14349             return provider;
14350         }
14351     };
14352 }();
14353 /*
14354  * Based on:
14355  * Ext JS Library 1.1.1
14356  * Copyright(c) 2006-2007, Ext JS, LLC.
14357  *
14358  * Originally Released Under LGPL - original licence link has changed is not relivant.
14359  *
14360  * Fork - LGPL
14361  * <script type="text/javascript">
14362  */
14363 /**
14364  * @class Roo.state.CookieProvider
14365  * @extends Roo.state.Provider
14366  * The default Provider implementation which saves state via cookies.
14367  * <br />Usage:
14368  <pre><code>
14369    var cp = new Roo.state.CookieProvider({
14370        path: "/cgi-bin/",
14371        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14372        domain: "roojs.com"
14373    })
14374    Roo.state.Manager.setProvider(cp);
14375  </code></pre>
14376  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14377  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14378  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14379  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14380  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14381  * domain the page is running on including the 'www' like 'www.roojs.com')
14382  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14383  * @constructor
14384  * Create a new CookieProvider
14385  * @param {Object} config The configuration object
14386  */
14387 Roo.state.CookieProvider = function(config){
14388     Roo.state.CookieProvider.superclass.constructor.call(this);
14389     this.path = "/";
14390     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14391     this.domain = null;
14392     this.secure = false;
14393     Roo.apply(this, config);
14394     this.state = this.readCookies();
14395 };
14396
14397 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14398     // private
14399     set : function(name, value){
14400         if(typeof value == "undefined" || value === null){
14401             this.clear(name);
14402             return;
14403         }
14404         this.setCookie(name, value);
14405         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14406     },
14407
14408     // private
14409     clear : function(name){
14410         this.clearCookie(name);
14411         Roo.state.CookieProvider.superclass.clear.call(this, name);
14412     },
14413
14414     // private
14415     readCookies : function(){
14416         var cookies = {};
14417         var c = document.cookie + ";";
14418         var re = /\s?(.*?)=(.*?);/g;
14419         var matches;
14420         while((matches = re.exec(c)) != null){
14421             var name = matches[1];
14422             var value = matches[2];
14423             if(name && name.substring(0,3) == "ys-"){
14424                 cookies[name.substr(3)] = this.decodeValue(value);
14425             }
14426         }
14427         return cookies;
14428     },
14429
14430     // private
14431     setCookie : function(name, value){
14432         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14433            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14434            ((this.path == null) ? "" : ("; path=" + this.path)) +
14435            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14436            ((this.secure == true) ? "; secure" : "");
14437     },
14438
14439     // private
14440     clearCookie : function(name){
14441         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14442            ((this.path == null) ? "" : ("; path=" + this.path)) +
14443            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14444            ((this.secure == true) ? "; secure" : "");
14445     }
14446 });/*
14447  * Based on:
14448  * Ext JS Library 1.1.1
14449  * Copyright(c) 2006-2007, Ext JS, LLC.
14450  *
14451  * Originally Released Under LGPL - original licence link has changed is not relivant.
14452  *
14453  * Fork - LGPL
14454  * <script type="text/javascript">
14455  */
14456
14457
14458
14459 /*
14460  * These classes are derivatives of the similarly named classes in the YUI Library.
14461  * The original license:
14462  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14463  * Code licensed under the BSD License:
14464  * http://developer.yahoo.net/yui/license.txt
14465  */
14466
14467 (function() {
14468
14469 var Event=Roo.EventManager;
14470 var Dom=Roo.lib.Dom;
14471
14472 /**
14473  * @class Roo.dd.DragDrop
14474  * @extends Roo.util.Observable
14475  * Defines the interface and base operation of items that that can be
14476  * dragged or can be drop targets.  It was designed to be extended, overriding
14477  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14478  * Up to three html elements can be associated with a DragDrop instance:
14479  * <ul>
14480  * <li>linked element: the element that is passed into the constructor.
14481  * This is the element which defines the boundaries for interaction with
14482  * other DragDrop objects.</li>
14483  * <li>handle element(s): The drag operation only occurs if the element that
14484  * was clicked matches a handle element.  By default this is the linked
14485  * element, but there are times that you will want only a portion of the
14486  * linked element to initiate the drag operation, and the setHandleElId()
14487  * method provides a way to define this.</li>
14488  * <li>drag element: this represents the element that would be moved along
14489  * with the cursor during a drag operation.  By default, this is the linked
14490  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14491  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14492  * </li>
14493  * </ul>
14494  * This class should not be instantiated until the onload event to ensure that
14495  * the associated elements are available.
14496  * The following would define a DragDrop obj that would interact with any
14497  * other DragDrop obj in the "group1" group:
14498  * <pre>
14499  *  dd = new Roo.dd.DragDrop("div1", "group1");
14500  * </pre>
14501  * Since none of the event handlers have been implemented, nothing would
14502  * actually happen if you were to run the code above.  Normally you would
14503  * override this class or one of the default implementations, but you can
14504  * also override the methods you want on an instance of the class...
14505  * <pre>
14506  *  dd.onDragDrop = function(e, id) {
14507  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14508  *  }
14509  * </pre>
14510  * @constructor
14511  * @param {String} id of the element that is linked to this instance
14512  * @param {String} sGroup the group of related DragDrop objects
14513  * @param {object} config an object containing configurable attributes
14514  *                Valid properties for DragDrop:
14515  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14516  */
14517 Roo.dd.DragDrop = function(id, sGroup, config) {
14518     if (id) {
14519         this.init(id, sGroup, config);
14520     }
14521     
14522 };
14523
14524 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14525
14526     /**
14527      * The id of the element associated with this object.  This is what we
14528      * refer to as the "linked element" because the size and position of
14529      * this element is used to determine when the drag and drop objects have
14530      * interacted.
14531      * @property id
14532      * @type String
14533      */
14534     id: null,
14535
14536     /**
14537      * Configuration attributes passed into the constructor
14538      * @property config
14539      * @type object
14540      */
14541     config: null,
14542
14543     /**
14544      * The id of the element that will be dragged.  By default this is same
14545      * as the linked element , but could be changed to another element. Ex:
14546      * Roo.dd.DDProxy
14547      * @property dragElId
14548      * @type String
14549      * @private
14550      */
14551     dragElId: null,
14552
14553     /**
14554      * the id of the element that initiates the drag operation.  By default
14555      * this is the linked element, but could be changed to be a child of this
14556      * element.  This lets us do things like only starting the drag when the
14557      * header element within the linked html element is clicked.
14558      * @property handleElId
14559      * @type String
14560      * @private
14561      */
14562     handleElId: null,
14563
14564     /**
14565      * An associative array of HTML tags that will be ignored if clicked.
14566      * @property invalidHandleTypes
14567      * @type {string: string}
14568      */
14569     invalidHandleTypes: null,
14570
14571     /**
14572      * An associative array of ids for elements that will be ignored if clicked
14573      * @property invalidHandleIds
14574      * @type {string: string}
14575      */
14576     invalidHandleIds: null,
14577
14578     /**
14579      * An indexted array of css class names for elements that will be ignored
14580      * if clicked.
14581      * @property invalidHandleClasses
14582      * @type string[]
14583      */
14584     invalidHandleClasses: null,
14585
14586     /**
14587      * The linked element's absolute X position at the time the drag was
14588      * started
14589      * @property startPageX
14590      * @type int
14591      * @private
14592      */
14593     startPageX: 0,
14594
14595     /**
14596      * The linked element's absolute X position at the time the drag was
14597      * started
14598      * @property startPageY
14599      * @type int
14600      * @private
14601      */
14602     startPageY: 0,
14603
14604     /**
14605      * The group defines a logical collection of DragDrop objects that are
14606      * related.  Instances only get events when interacting with other
14607      * DragDrop object in the same group.  This lets us define multiple
14608      * groups using a single DragDrop subclass if we want.
14609      * @property groups
14610      * @type {string: string}
14611      */
14612     groups: null,
14613
14614     /**
14615      * Individual drag/drop instances can be locked.  This will prevent
14616      * onmousedown start drag.
14617      * @property locked
14618      * @type boolean
14619      * @private
14620      */
14621     locked: false,
14622
14623     /**
14624      * Lock this instance
14625      * @method lock
14626      */
14627     lock: function() { this.locked = true; },
14628
14629     /**
14630      * Unlock this instace
14631      * @method unlock
14632      */
14633     unlock: function() { this.locked = false; },
14634
14635     /**
14636      * By default, all insances can be a drop target.  This can be disabled by
14637      * setting isTarget to false.
14638      * @method isTarget
14639      * @type boolean
14640      */
14641     isTarget: true,
14642
14643     /**
14644      * The padding configured for this drag and drop object for calculating
14645      * the drop zone intersection with this object.
14646      * @method padding
14647      * @type int[]
14648      */
14649     padding: null,
14650
14651     /**
14652      * Cached reference to the linked element
14653      * @property _domRef
14654      * @private
14655      */
14656     _domRef: null,
14657
14658     /**
14659      * Internal typeof flag
14660      * @property __ygDragDrop
14661      * @private
14662      */
14663     __ygDragDrop: true,
14664
14665     /**
14666      * Set to true when horizontal contraints are applied
14667      * @property constrainX
14668      * @type boolean
14669      * @private
14670      */
14671     constrainX: false,
14672
14673     /**
14674      * Set to true when vertical contraints are applied
14675      * @property constrainY
14676      * @type boolean
14677      * @private
14678      */
14679     constrainY: false,
14680
14681     /**
14682      * The left constraint
14683      * @property minX
14684      * @type int
14685      * @private
14686      */
14687     minX: 0,
14688
14689     /**
14690      * The right constraint
14691      * @property maxX
14692      * @type int
14693      * @private
14694      */
14695     maxX: 0,
14696
14697     /**
14698      * The up constraint
14699      * @property minY
14700      * @type int
14701      * @type int
14702      * @private
14703      */
14704     minY: 0,
14705
14706     /**
14707      * The down constraint
14708      * @property maxY
14709      * @type int
14710      * @private
14711      */
14712     maxY: 0,
14713
14714     /**
14715      * Maintain offsets when we resetconstraints.  Set to true when you want
14716      * the position of the element relative to its parent to stay the same
14717      * when the page changes
14718      *
14719      * @property maintainOffset
14720      * @type boolean
14721      */
14722     maintainOffset: false,
14723
14724     /**
14725      * Array of pixel locations the element will snap to if we specified a
14726      * horizontal graduation/interval.  This array is generated automatically
14727      * when you define a tick interval.
14728      * @property xTicks
14729      * @type int[]
14730      */
14731     xTicks: null,
14732
14733     /**
14734      * Array of pixel locations the element will snap to if we specified a
14735      * vertical graduation/interval.  This array is generated automatically
14736      * when you define a tick interval.
14737      * @property yTicks
14738      * @type int[]
14739      */
14740     yTicks: null,
14741
14742     /**
14743      * By default the drag and drop instance will only respond to the primary
14744      * button click (left button for a right-handed mouse).  Set to true to
14745      * allow drag and drop to start with any mouse click that is propogated
14746      * by the browser
14747      * @property primaryButtonOnly
14748      * @type boolean
14749      */
14750     primaryButtonOnly: true,
14751
14752     /**
14753      * The availabe property is false until the linked dom element is accessible.
14754      * @property available
14755      * @type boolean
14756      */
14757     available: false,
14758
14759     /**
14760      * By default, drags can only be initiated if the mousedown occurs in the
14761      * region the linked element is.  This is done in part to work around a
14762      * bug in some browsers that mis-report the mousedown if the previous
14763      * mouseup happened outside of the window.  This property is set to true
14764      * if outer handles are defined.
14765      *
14766      * @property hasOuterHandles
14767      * @type boolean
14768      * @default false
14769      */
14770     hasOuterHandles: false,
14771
14772     /**
14773      * Code that executes immediately before the startDrag event
14774      * @method b4StartDrag
14775      * @private
14776      */
14777     b4StartDrag: function(x, y) { },
14778
14779     /**
14780      * Abstract method called after a drag/drop object is clicked
14781      * and the drag or mousedown time thresholds have beeen met.
14782      * @method startDrag
14783      * @param {int} X click location
14784      * @param {int} Y click location
14785      */
14786     startDrag: function(x, y) { /* override this */ },
14787
14788     /**
14789      * Code that executes immediately before the onDrag event
14790      * @method b4Drag
14791      * @private
14792      */
14793     b4Drag: function(e) { },
14794
14795     /**
14796      * Abstract method called during the onMouseMove event while dragging an
14797      * object.
14798      * @method onDrag
14799      * @param {Event} e the mousemove event
14800      */
14801     onDrag: function(e) { /* override this */ },
14802
14803     /**
14804      * Abstract method called when this element fist begins hovering over
14805      * another DragDrop obj
14806      * @method onDragEnter
14807      * @param {Event} e the mousemove event
14808      * @param {String|DragDrop[]} id In POINT mode, the element
14809      * id this is hovering over.  In INTERSECT mode, an array of one or more
14810      * dragdrop items being hovered over.
14811      */
14812     onDragEnter: function(e, id) { /* override this */ },
14813
14814     /**
14815      * Code that executes immediately before the onDragOver event
14816      * @method b4DragOver
14817      * @private
14818      */
14819     b4DragOver: function(e) { },
14820
14821     /**
14822      * Abstract method called when this element is hovering over another
14823      * DragDrop obj
14824      * @method onDragOver
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 dd items
14828      * being hovered over.
14829      */
14830     onDragOver: function(e, id) { /* override this */ },
14831
14832     /**
14833      * Code that executes immediately before the onDragOut event
14834      * @method b4DragOut
14835      * @private
14836      */
14837     b4DragOut: function(e) { },
14838
14839     /**
14840      * Abstract method called when we are no longer hovering over an element
14841      * @method onDragOut
14842      * @param {Event} e the mousemove event
14843      * @param {String|DragDrop[]} id In POINT mode, the element
14844      * id this was hovering over.  In INTERSECT mode, an array of dd items
14845      * that the mouse is no longer over.
14846      */
14847     onDragOut: function(e, id) { /* override this */ },
14848
14849     /**
14850      * Code that executes immediately before the onDragDrop event
14851      * @method b4DragDrop
14852      * @private
14853      */
14854     b4DragDrop: function(e) { },
14855
14856     /**
14857      * Abstract method called when this item is dropped on another DragDrop
14858      * obj
14859      * @method onDragDrop
14860      * @param {Event} e the mouseup event
14861      * @param {String|DragDrop[]} id In POINT mode, the element
14862      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14863      * was dropped on.
14864      */
14865     onDragDrop: function(e, id) { /* override this */ },
14866
14867     /**
14868      * Abstract method called when this item is dropped on an area with no
14869      * drop target
14870      * @method onInvalidDrop
14871      * @param {Event} e the mouseup event
14872      */
14873     onInvalidDrop: function(e) { /* override this */ },
14874
14875     /**
14876      * Code that executes immediately before the endDrag event
14877      * @method b4EndDrag
14878      * @private
14879      */
14880     b4EndDrag: function(e) { },
14881
14882     /**
14883      * Fired when we are done dragging the object
14884      * @method endDrag
14885      * @param {Event} e the mouseup event
14886      */
14887     endDrag: function(e) { /* override this */ },
14888
14889     /**
14890      * Code executed immediately before the onMouseDown event
14891      * @method b4MouseDown
14892      * @param {Event} e the mousedown event
14893      * @private
14894      */
14895     b4MouseDown: function(e) {  },
14896
14897     /**
14898      * Event handler that fires when a drag/drop obj gets a mousedown
14899      * @method onMouseDown
14900      * @param {Event} e the mousedown event
14901      */
14902     onMouseDown: function(e) { /* override this */ },
14903
14904     /**
14905      * Event handler that fires when a drag/drop obj gets a mouseup
14906      * @method onMouseUp
14907      * @param {Event} e the mouseup event
14908      */
14909     onMouseUp: function(e) { /* override this */ },
14910
14911     /**
14912      * Override the onAvailable method to do what is needed after the initial
14913      * position was determined.
14914      * @method onAvailable
14915      */
14916     onAvailable: function () {
14917     },
14918
14919     /*
14920      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14921      * @type Object
14922      */
14923     defaultPadding : {left:0, right:0, top:0, bottom:0},
14924
14925     /*
14926      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14927  *
14928  * Usage:
14929  <pre><code>
14930  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14931                 { dragElId: "existingProxyDiv" });
14932  dd.startDrag = function(){
14933      this.constrainTo("parent-id");
14934  };
14935  </code></pre>
14936  * Or you can initalize it using the {@link Roo.Element} object:
14937  <pre><code>
14938  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14939      startDrag : function(){
14940          this.constrainTo("parent-id");
14941      }
14942  });
14943  </code></pre>
14944      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14945      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14946      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14947      * an object containing the sides to pad. For example: {right:10, bottom:10}
14948      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14949      */
14950     constrainTo : function(constrainTo, pad, inContent){
14951         if(typeof pad == "number"){
14952             pad = {left: pad, right:pad, top:pad, bottom:pad};
14953         }
14954         pad = pad || this.defaultPadding;
14955         var b = Roo.get(this.getEl()).getBox();
14956         var ce = Roo.get(constrainTo);
14957         var s = ce.getScroll();
14958         var c, cd = ce.dom;
14959         if(cd == document.body){
14960             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14961         }else{
14962             xy = ce.getXY();
14963             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14964         }
14965
14966
14967         var topSpace = b.y - c.y;
14968         var leftSpace = b.x - c.x;
14969
14970         this.resetConstraints();
14971         this.setXConstraint(leftSpace - (pad.left||0), // left
14972                 c.width - leftSpace - b.width - (pad.right||0) //right
14973         );
14974         this.setYConstraint(topSpace - (pad.top||0), //top
14975                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14976         );
14977     },
14978
14979     /**
14980      * Returns a reference to the linked element
14981      * @method getEl
14982      * @return {HTMLElement} the html element
14983      */
14984     getEl: function() {
14985         if (!this._domRef) {
14986             this._domRef = Roo.getDom(this.id);
14987         }
14988
14989         return this._domRef;
14990     },
14991
14992     /**
14993      * Returns a reference to the actual element to drag.  By default this is
14994      * the same as the html element, but it can be assigned to another
14995      * element. An example of this can be found in Roo.dd.DDProxy
14996      * @method getDragEl
14997      * @return {HTMLElement} the html element
14998      */
14999     getDragEl: function() {
15000         return Roo.getDom(this.dragElId);
15001     },
15002
15003     /**
15004      * Sets up the DragDrop object.  Must be called in the constructor of any
15005      * Roo.dd.DragDrop subclass
15006      * @method init
15007      * @param id the id of the linked element
15008      * @param {String} sGroup the group of related items
15009      * @param {object} config configuration attributes
15010      */
15011     init: function(id, sGroup, config) {
15012         this.initTarget(id, sGroup, config);
15013         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15014         // Event.on(this.id, "selectstart", Event.preventDefault);
15015     },
15016
15017     /**
15018      * Initializes Targeting functionality only... the object does not
15019      * get a mousedown handler.
15020      * @method initTarget
15021      * @param id the id of the linked element
15022      * @param {String} sGroup the group of related items
15023      * @param {object} config configuration attributes
15024      */
15025     initTarget: function(id, sGroup, config) {
15026
15027         // configuration attributes
15028         this.config = config || {};
15029
15030         // create a local reference to the drag and drop manager
15031         this.DDM = Roo.dd.DDM;
15032         // initialize the groups array
15033         this.groups = {};
15034
15035         // assume that we have an element reference instead of an id if the
15036         // parameter is not a string
15037         if (typeof id !== "string") {
15038             id = Roo.id(id);
15039         }
15040
15041         // set the id
15042         this.id = id;
15043
15044         // add to an interaction group
15045         this.addToGroup((sGroup) ? sGroup : "default");
15046
15047         // We don't want to register this as the handle with the manager
15048         // so we just set the id rather than calling the setter.
15049         this.handleElId = id;
15050
15051         // the linked element is the element that gets dragged by default
15052         this.setDragElId(id);
15053
15054         // by default, clicked anchors will not start drag operations.
15055         this.invalidHandleTypes = { A: "A" };
15056         this.invalidHandleIds = {};
15057         this.invalidHandleClasses = [];
15058
15059         this.applyConfig();
15060
15061         this.handleOnAvailable();
15062     },
15063
15064     /**
15065      * Applies the configuration parameters that were passed into the constructor.
15066      * This is supposed to happen at each level through the inheritance chain.  So
15067      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15068      * DragDrop in order to get all of the parameters that are available in
15069      * each object.
15070      * @method applyConfig
15071      */
15072     applyConfig: function() {
15073
15074         // configurable properties:
15075         //    padding, isTarget, maintainOffset, primaryButtonOnly
15076         this.padding           = this.config.padding || [0, 0, 0, 0];
15077         this.isTarget          = (this.config.isTarget !== false);
15078         this.maintainOffset    = (this.config.maintainOffset);
15079         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15080
15081     },
15082
15083     /**
15084      * Executed when the linked element is available
15085      * @method handleOnAvailable
15086      * @private
15087      */
15088     handleOnAvailable: function() {
15089         this.available = true;
15090         this.resetConstraints();
15091         this.onAvailable();
15092     },
15093
15094      /**
15095      * Configures the padding for the target zone in px.  Effectively expands
15096      * (or reduces) the virtual object size for targeting calculations.
15097      * Supports css-style shorthand; if only one parameter is passed, all sides
15098      * will have that padding, and if only two are passed, the top and bottom
15099      * will have the first param, the left and right the second.
15100      * @method setPadding
15101      * @param {int} iTop    Top pad
15102      * @param {int} iRight  Right pad
15103      * @param {int} iBot    Bot pad
15104      * @param {int} iLeft   Left pad
15105      */
15106     setPadding: function(iTop, iRight, iBot, iLeft) {
15107         // this.padding = [iLeft, iRight, iTop, iBot];
15108         if (!iRight && 0 !== iRight) {
15109             this.padding = [iTop, iTop, iTop, iTop];
15110         } else if (!iBot && 0 !== iBot) {
15111             this.padding = [iTop, iRight, iTop, iRight];
15112         } else {
15113             this.padding = [iTop, iRight, iBot, iLeft];
15114         }
15115     },
15116
15117     /**
15118      * Stores the initial placement of the linked element.
15119      * @method setInitialPosition
15120      * @param {int} diffX   the X offset, default 0
15121      * @param {int} diffY   the Y offset, default 0
15122      */
15123     setInitPosition: function(diffX, diffY) {
15124         var el = this.getEl();
15125
15126         if (!this.DDM.verifyEl(el)) {
15127             return;
15128         }
15129
15130         var dx = diffX || 0;
15131         var dy = diffY || 0;
15132
15133         var p = Dom.getXY( el );
15134
15135         this.initPageX = p[0] - dx;
15136         this.initPageY = p[1] - dy;
15137
15138         this.lastPageX = p[0];
15139         this.lastPageY = p[1];
15140
15141
15142         this.setStartPosition(p);
15143     },
15144
15145     /**
15146      * Sets the start position of the element.  This is set when the obj
15147      * is initialized, the reset when a drag is started.
15148      * @method setStartPosition
15149      * @param pos current position (from previous lookup)
15150      * @private
15151      */
15152     setStartPosition: function(pos) {
15153         var p = pos || Dom.getXY( this.getEl() );
15154         this.deltaSetXY = null;
15155
15156         this.startPageX = p[0];
15157         this.startPageY = p[1];
15158     },
15159
15160     /**
15161      * Add this instance to a group of related drag/drop objects.  All
15162      * instances belong to at least one group, and can belong to as many
15163      * groups as needed.
15164      * @method addToGroup
15165      * @param sGroup {string} the name of the group
15166      */
15167     addToGroup: function(sGroup) {
15168         this.groups[sGroup] = true;
15169         this.DDM.regDragDrop(this, sGroup);
15170     },
15171
15172     /**
15173      * Remove's this instance from the supplied interaction group
15174      * @method removeFromGroup
15175      * @param {string}  sGroup  The group to drop
15176      */
15177     removeFromGroup: function(sGroup) {
15178         if (this.groups[sGroup]) {
15179             delete this.groups[sGroup];
15180         }
15181
15182         this.DDM.removeDDFromGroup(this, sGroup);
15183     },
15184
15185     /**
15186      * Allows you to specify that an element other than the linked element
15187      * will be moved with the cursor during a drag
15188      * @method setDragElId
15189      * @param id {string} the id of the element that will be used to initiate the drag
15190      */
15191     setDragElId: function(id) {
15192         this.dragElId = id;
15193     },
15194
15195     /**
15196      * Allows you to specify a child of the linked element that should be
15197      * used to initiate the drag operation.  An example of this would be if
15198      * you have a content div with text and links.  Clicking anywhere in the
15199      * content area would normally start the drag operation.  Use this method
15200      * to specify that an element inside of the content div is the element
15201      * that starts the drag operation.
15202      * @method setHandleElId
15203      * @param id {string} the id of the element that will be used to
15204      * initiate the drag.
15205      */
15206     setHandleElId: function(id) {
15207         if (typeof id !== "string") {
15208             id = Roo.id(id);
15209         }
15210         this.handleElId = id;
15211         this.DDM.regHandle(this.id, id);
15212     },
15213
15214     /**
15215      * Allows you to set an element outside of the linked element as a drag
15216      * handle
15217      * @method setOuterHandleElId
15218      * @param id the id of the element that will be used to initiate the drag
15219      */
15220     setOuterHandleElId: function(id) {
15221         if (typeof id !== "string") {
15222             id = Roo.id(id);
15223         }
15224         Event.on(id, "mousedown",
15225                 this.handleMouseDown, this);
15226         this.setHandleElId(id);
15227
15228         this.hasOuterHandles = true;
15229     },
15230
15231     /**
15232      * Remove all drag and drop hooks for this element
15233      * @method unreg
15234      */
15235     unreg: function() {
15236         Event.un(this.id, "mousedown",
15237                 this.handleMouseDown);
15238         this._domRef = null;
15239         this.DDM._remove(this);
15240     },
15241
15242     destroy : function(){
15243         this.unreg();
15244     },
15245
15246     /**
15247      * Returns true if this instance is locked, or the drag drop mgr is locked
15248      * (meaning that all drag/drop is disabled on the page.)
15249      * @method isLocked
15250      * @return {boolean} true if this obj or all drag/drop is locked, else
15251      * false
15252      */
15253     isLocked: function() {
15254         return (this.DDM.isLocked() || this.locked);
15255     },
15256
15257     /**
15258      * Fired when this object is clicked
15259      * @method handleMouseDown
15260      * @param {Event} e
15261      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15262      * @private
15263      */
15264     handleMouseDown: function(e, oDD){
15265         if (this.primaryButtonOnly && e.button != 0) {
15266             return;
15267         }
15268
15269         if (this.isLocked()) {
15270             return;
15271         }
15272
15273         this.DDM.refreshCache(this.groups);
15274
15275         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15276         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15277         } else {
15278             if (this.clickValidator(e)) {
15279
15280                 // set the initial element position
15281                 this.setStartPosition();
15282
15283
15284                 this.b4MouseDown(e);
15285                 this.onMouseDown(e);
15286
15287                 this.DDM.handleMouseDown(e, this);
15288
15289                 this.DDM.stopEvent(e);
15290             } else {
15291
15292
15293             }
15294         }
15295     },
15296
15297     clickValidator: function(e) {
15298         var target = e.getTarget();
15299         return ( this.isValidHandleChild(target) &&
15300                     (this.id == this.handleElId ||
15301                         this.DDM.handleWasClicked(target, this.id)) );
15302     },
15303
15304     /**
15305      * Allows you to specify a tag name that should not start a drag operation
15306      * when clicked.  This is designed to facilitate embedding links within a
15307      * drag handle that do something other than start the drag.
15308      * @method addInvalidHandleType
15309      * @param {string} tagName the type of element to exclude
15310      */
15311     addInvalidHandleType: function(tagName) {
15312         var type = tagName.toUpperCase();
15313         this.invalidHandleTypes[type] = type;
15314     },
15315
15316     /**
15317      * Lets you to specify an element id for a child of a drag handle
15318      * that should not initiate a drag
15319      * @method addInvalidHandleId
15320      * @param {string} id the element id of the element you wish to ignore
15321      */
15322     addInvalidHandleId: function(id) {
15323         if (typeof id !== "string") {
15324             id = Roo.id(id);
15325         }
15326         this.invalidHandleIds[id] = id;
15327     },
15328
15329     /**
15330      * Lets you specify a css class of elements that will not initiate a drag
15331      * @method addInvalidHandleClass
15332      * @param {string} cssClass the class of the elements you wish to ignore
15333      */
15334     addInvalidHandleClass: function(cssClass) {
15335         this.invalidHandleClasses.push(cssClass);
15336     },
15337
15338     /**
15339      * Unsets an excluded tag name set by addInvalidHandleType
15340      * @method removeInvalidHandleType
15341      * @param {string} tagName the type of element to unexclude
15342      */
15343     removeInvalidHandleType: function(tagName) {
15344         var type = tagName.toUpperCase();
15345         // this.invalidHandleTypes[type] = null;
15346         delete this.invalidHandleTypes[type];
15347     },
15348
15349     /**
15350      * Unsets an invalid handle id
15351      * @method removeInvalidHandleId
15352      * @param {string} id the id of the element to re-enable
15353      */
15354     removeInvalidHandleId: function(id) {
15355         if (typeof id !== "string") {
15356             id = Roo.id(id);
15357         }
15358         delete this.invalidHandleIds[id];
15359     },
15360
15361     /**
15362      * Unsets an invalid css class
15363      * @method removeInvalidHandleClass
15364      * @param {string} cssClass the class of the element(s) you wish to
15365      * re-enable
15366      */
15367     removeInvalidHandleClass: function(cssClass) {
15368         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15369             if (this.invalidHandleClasses[i] == cssClass) {
15370                 delete this.invalidHandleClasses[i];
15371             }
15372         }
15373     },
15374
15375     /**
15376      * Checks the tag exclusion list to see if this click should be ignored
15377      * @method isValidHandleChild
15378      * @param {HTMLElement} node the HTMLElement to evaluate
15379      * @return {boolean} true if this is a valid tag type, false if not
15380      */
15381     isValidHandleChild: function(node) {
15382
15383         var valid = true;
15384         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15385         var nodeName;
15386         try {
15387             nodeName = node.nodeName.toUpperCase();
15388         } catch(e) {
15389             nodeName = node.nodeName;
15390         }
15391         valid = valid && !this.invalidHandleTypes[nodeName];
15392         valid = valid && !this.invalidHandleIds[node.id];
15393
15394         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15395             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15396         }
15397
15398
15399         return valid;
15400
15401     },
15402
15403     /**
15404      * Create the array of horizontal tick marks if an interval was specified
15405      * in setXConstraint().
15406      * @method setXTicks
15407      * @private
15408      */
15409     setXTicks: function(iStartX, iTickSize) {
15410         this.xTicks = [];
15411         this.xTickSize = iTickSize;
15412
15413         var tickMap = {};
15414
15415         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15416             if (!tickMap[i]) {
15417                 this.xTicks[this.xTicks.length] = i;
15418                 tickMap[i] = true;
15419             }
15420         }
15421
15422         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15423             if (!tickMap[i]) {
15424                 this.xTicks[this.xTicks.length] = i;
15425                 tickMap[i] = true;
15426             }
15427         }
15428
15429         this.xTicks.sort(this.DDM.numericSort) ;
15430     },
15431
15432     /**
15433      * Create the array of vertical tick marks if an interval was specified in
15434      * setYConstraint().
15435      * @method setYTicks
15436      * @private
15437      */
15438     setYTicks: function(iStartY, iTickSize) {
15439         this.yTicks = [];
15440         this.yTickSize = iTickSize;
15441
15442         var tickMap = {};
15443
15444         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15445             if (!tickMap[i]) {
15446                 this.yTicks[this.yTicks.length] = i;
15447                 tickMap[i] = true;
15448             }
15449         }
15450
15451         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15452             if (!tickMap[i]) {
15453                 this.yTicks[this.yTicks.length] = i;
15454                 tickMap[i] = true;
15455             }
15456         }
15457
15458         this.yTicks.sort(this.DDM.numericSort) ;
15459     },
15460
15461     /**
15462      * By default, the element can be dragged any place on the screen.  Use
15463      * this method to limit the horizontal travel of the element.  Pass in
15464      * 0,0 for the parameters if you want to lock the drag to the y axis.
15465      * @method setXConstraint
15466      * @param {int} iLeft the number of pixels the element can move to the left
15467      * @param {int} iRight the number of pixels the element can move to the
15468      * right
15469      * @param {int} iTickSize optional parameter for specifying that the
15470      * element
15471      * should move iTickSize pixels at a time.
15472      */
15473     setXConstraint: function(iLeft, iRight, iTickSize) {
15474         this.leftConstraint = iLeft;
15475         this.rightConstraint = iRight;
15476
15477         this.minX = this.initPageX - iLeft;
15478         this.maxX = this.initPageX + iRight;
15479         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15480
15481         this.constrainX = true;
15482     },
15483
15484     /**
15485      * Clears any constraints applied to this instance.  Also clears ticks
15486      * since they can't exist independent of a constraint at this time.
15487      * @method clearConstraints
15488      */
15489     clearConstraints: function() {
15490         this.constrainX = false;
15491         this.constrainY = false;
15492         this.clearTicks();
15493     },
15494
15495     /**
15496      * Clears any tick interval defined for this instance
15497      * @method clearTicks
15498      */
15499     clearTicks: function() {
15500         this.xTicks = null;
15501         this.yTicks = null;
15502         this.xTickSize = 0;
15503         this.yTickSize = 0;
15504     },
15505
15506     /**
15507      * By default, the element can be dragged any place on the screen.  Set
15508      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15509      * parameters if you want to lock the drag to the x axis.
15510      * @method setYConstraint
15511      * @param {int} iUp the number of pixels the element can move up
15512      * @param {int} iDown the number of pixels the element can move down
15513      * @param {int} iTickSize optional parameter for specifying that the
15514      * element should move iTickSize pixels at a time.
15515      */
15516     setYConstraint: function(iUp, iDown, iTickSize) {
15517         this.topConstraint = iUp;
15518         this.bottomConstraint = iDown;
15519
15520         this.minY = this.initPageY - iUp;
15521         this.maxY = this.initPageY + iDown;
15522         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15523
15524         this.constrainY = true;
15525
15526     },
15527
15528     /**
15529      * resetConstraints must be called if you manually reposition a dd element.
15530      * @method resetConstraints
15531      * @param {boolean} maintainOffset
15532      */
15533     resetConstraints: function() {
15534
15535
15536         // Maintain offsets if necessary
15537         if (this.initPageX || this.initPageX === 0) {
15538             // figure out how much this thing has moved
15539             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15540             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15541
15542             this.setInitPosition(dx, dy);
15543
15544         // This is the first time we have detected the element's position
15545         } else {
15546             this.setInitPosition();
15547         }
15548
15549         if (this.constrainX) {
15550             this.setXConstraint( this.leftConstraint,
15551                                  this.rightConstraint,
15552                                  this.xTickSize        );
15553         }
15554
15555         if (this.constrainY) {
15556             this.setYConstraint( this.topConstraint,
15557                                  this.bottomConstraint,
15558                                  this.yTickSize         );
15559         }
15560     },
15561
15562     /**
15563      * Normally the drag element is moved pixel by pixel, but we can specify
15564      * that it move a number of pixels at a time.  This method resolves the
15565      * location when we have it set up like this.
15566      * @method getTick
15567      * @param {int} val where we want to place the object
15568      * @param {int[]} tickArray sorted array of valid points
15569      * @return {int} the closest tick
15570      * @private
15571      */
15572     getTick: function(val, tickArray) {
15573
15574         if (!tickArray) {
15575             // If tick interval is not defined, it is effectively 1 pixel,
15576             // so we return the value passed to us.
15577             return val;
15578         } else if (tickArray[0] >= val) {
15579             // The value is lower than the first tick, so we return the first
15580             // tick.
15581             return tickArray[0];
15582         } else {
15583             for (var i=0, len=tickArray.length; i<len; ++i) {
15584                 var next = i + 1;
15585                 if (tickArray[next] && tickArray[next] >= val) {
15586                     var diff1 = val - tickArray[i];
15587                     var diff2 = tickArray[next] - val;
15588                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15589                 }
15590             }
15591
15592             // The value is larger than the last tick, so we return the last
15593             // tick.
15594             return tickArray[tickArray.length - 1];
15595         }
15596     },
15597
15598     /**
15599      * toString method
15600      * @method toString
15601      * @return {string} string representation of the dd obj
15602      */
15603     toString: function() {
15604         return ("DragDrop " + this.id);
15605     }
15606
15607 });
15608
15609 })();
15610 /*
15611  * Based on:
15612  * Ext JS Library 1.1.1
15613  * Copyright(c) 2006-2007, Ext JS, LLC.
15614  *
15615  * Originally Released Under LGPL - original licence link has changed is not relivant.
15616  *
15617  * Fork - LGPL
15618  * <script type="text/javascript">
15619  */
15620
15621
15622 /**
15623  * The drag and drop utility provides a framework for building drag and drop
15624  * applications.  In addition to enabling drag and drop for specific elements,
15625  * the drag and drop elements are tracked by the manager class, and the
15626  * interactions between the various elements are tracked during the drag and
15627  * the implementing code is notified about these important moments.
15628  */
15629
15630 // Only load the library once.  Rewriting the manager class would orphan
15631 // existing drag and drop instances.
15632 if (!Roo.dd.DragDropMgr) {
15633
15634 /**
15635  * @class Roo.dd.DragDropMgr
15636  * DragDropMgr is a singleton that tracks the element interaction for
15637  * all DragDrop items in the window.  Generally, you will not call
15638  * this class directly, but it does have helper methods that could
15639  * be useful in your DragDrop implementations.
15640  * @singleton
15641  */
15642 Roo.dd.DragDropMgr = function() {
15643
15644     var Event = Roo.EventManager;
15645
15646     return {
15647
15648         /**
15649          * Two dimensional Array of registered DragDrop objects.  The first
15650          * dimension is the DragDrop item group, the second the DragDrop
15651          * object.
15652          * @property ids
15653          * @type {string: string}
15654          * @private
15655          * @static
15656          */
15657         ids: {},
15658
15659         /**
15660          * Array of element ids defined as drag handles.  Used to determine
15661          * if the element that generated the mousedown event is actually the
15662          * handle and not the html element itself.
15663          * @property handleIds
15664          * @type {string: string}
15665          * @private
15666          * @static
15667          */
15668         handleIds: {},
15669
15670         /**
15671          * the DragDrop object that is currently being dragged
15672          * @property dragCurrent
15673          * @type DragDrop
15674          * @private
15675          * @static
15676          **/
15677         dragCurrent: null,
15678
15679         /**
15680          * the DragDrop object(s) that are being hovered over
15681          * @property dragOvers
15682          * @type Array
15683          * @private
15684          * @static
15685          */
15686         dragOvers: {},
15687
15688         /**
15689          * the X distance between the cursor and the object being dragged
15690          * @property deltaX
15691          * @type int
15692          * @private
15693          * @static
15694          */
15695         deltaX: 0,
15696
15697         /**
15698          * the Y distance between the cursor and the object being dragged
15699          * @property deltaY
15700          * @type int
15701          * @private
15702          * @static
15703          */
15704         deltaY: 0,
15705
15706         /**
15707          * Flag to determine if we should prevent the default behavior of the
15708          * events we define. By default this is true, but this can be set to
15709          * false if you need the default behavior (not recommended)
15710          * @property preventDefault
15711          * @type boolean
15712          * @static
15713          */
15714         preventDefault: true,
15715
15716         /**
15717          * Flag to determine if we should stop the propagation of the events
15718          * we generate. This is true by default but you may want to set it to
15719          * false if the html element contains other features that require the
15720          * mouse click.
15721          * @property stopPropagation
15722          * @type boolean
15723          * @static
15724          */
15725         stopPropagation: true,
15726
15727         /**
15728          * Internal flag that is set to true when drag and drop has been
15729          * intialized
15730          * @property initialized
15731          * @private
15732          * @static
15733          */
15734         initalized: false,
15735
15736         /**
15737          * All drag and drop can be disabled.
15738          * @property locked
15739          * @private
15740          * @static
15741          */
15742         locked: false,
15743
15744         /**
15745          * Called the first time an element is registered.
15746          * @method init
15747          * @private
15748          * @static
15749          */
15750         init: function() {
15751             this.initialized = true;
15752         },
15753
15754         /**
15755          * In point mode, drag and drop interaction is defined by the
15756          * location of the cursor during the drag/drop
15757          * @property POINT
15758          * @type int
15759          * @static
15760          */
15761         POINT: 0,
15762
15763         /**
15764          * In intersect mode, drag and drop interactio nis defined by the
15765          * overlap of two or more drag and drop objects.
15766          * @property INTERSECT
15767          * @type int
15768          * @static
15769          */
15770         INTERSECT: 1,
15771
15772         /**
15773          * The current drag and drop mode.  Default: POINT
15774          * @property mode
15775          * @type int
15776          * @static
15777          */
15778         mode: 0,
15779
15780         /**
15781          * Runs method on all drag and drop objects
15782          * @method _execOnAll
15783          * @private
15784          * @static
15785          */
15786         _execOnAll: function(sMethod, args) {
15787             for (var i in this.ids) {
15788                 for (var j in this.ids[i]) {
15789                     var oDD = this.ids[i][j];
15790                     if (! this.isTypeOfDD(oDD)) {
15791                         continue;
15792                     }
15793                     oDD[sMethod].apply(oDD, args);
15794                 }
15795             }
15796         },
15797
15798         /**
15799          * Drag and drop initialization.  Sets up the global event handlers
15800          * @method _onLoad
15801          * @private
15802          * @static
15803          */
15804         _onLoad: function() {
15805
15806             this.init();
15807
15808
15809             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15810             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15811             Event.on(window,   "unload",    this._onUnload, this, true);
15812             Event.on(window,   "resize",    this._onResize, this, true);
15813             // Event.on(window,   "mouseout",    this._test);
15814
15815         },
15816
15817         /**
15818          * Reset constraints on all drag and drop objs
15819          * @method _onResize
15820          * @private
15821          * @static
15822          */
15823         _onResize: function(e) {
15824             this._execOnAll("resetConstraints", []);
15825         },
15826
15827         /**
15828          * Lock all drag and drop functionality
15829          * @method lock
15830          * @static
15831          */
15832         lock: function() { this.locked = true; },
15833
15834         /**
15835          * Unlock all drag and drop functionality
15836          * @method unlock
15837          * @static
15838          */
15839         unlock: function() { this.locked = false; },
15840
15841         /**
15842          * Is drag and drop locked?
15843          * @method isLocked
15844          * @return {boolean} True if drag and drop is locked, false otherwise.
15845          * @static
15846          */
15847         isLocked: function() { return this.locked; },
15848
15849         /**
15850          * Location cache that is set for all drag drop objects when a drag is
15851          * initiated, cleared when the drag is finished.
15852          * @property locationCache
15853          * @private
15854          * @static
15855          */
15856         locationCache: {},
15857
15858         /**
15859          * Set useCache to false if you want to force object the lookup of each
15860          * drag and drop linked element constantly during a drag.
15861          * @property useCache
15862          * @type boolean
15863          * @static
15864          */
15865         useCache: true,
15866
15867         /**
15868          * The number of pixels that the mouse needs to move after the
15869          * mousedown before the drag is initiated.  Default=3;
15870          * @property clickPixelThresh
15871          * @type int
15872          * @static
15873          */
15874         clickPixelThresh: 3,
15875
15876         /**
15877          * The number of milliseconds after the mousedown event to initiate the
15878          * drag if we don't get a mouseup event. Default=1000
15879          * @property clickTimeThresh
15880          * @type int
15881          * @static
15882          */
15883         clickTimeThresh: 350,
15884
15885         /**
15886          * Flag that indicates that either the drag pixel threshold or the
15887          * mousdown time threshold has been met
15888          * @property dragThreshMet
15889          * @type boolean
15890          * @private
15891          * @static
15892          */
15893         dragThreshMet: false,
15894
15895         /**
15896          * Timeout used for the click time threshold
15897          * @property clickTimeout
15898          * @type Object
15899          * @private
15900          * @static
15901          */
15902         clickTimeout: null,
15903
15904         /**
15905          * The X position of the mousedown event stored for later use when a
15906          * drag threshold is met.
15907          * @property startX
15908          * @type int
15909          * @private
15910          * @static
15911          */
15912         startX: 0,
15913
15914         /**
15915          * The Y position of the mousedown event stored for later use when a
15916          * drag threshold is met.
15917          * @property startY
15918          * @type int
15919          * @private
15920          * @static
15921          */
15922         startY: 0,
15923
15924         /**
15925          * Each DragDrop instance must be registered with the DragDropMgr.
15926          * This is executed in DragDrop.init()
15927          * @method regDragDrop
15928          * @param {DragDrop} oDD the DragDrop object to register
15929          * @param {String} sGroup the name of the group this element belongs to
15930          * @static
15931          */
15932         regDragDrop: function(oDD, sGroup) {
15933             if (!this.initialized) { this.init(); }
15934
15935             if (!this.ids[sGroup]) {
15936                 this.ids[sGroup] = {};
15937             }
15938             this.ids[sGroup][oDD.id] = oDD;
15939         },
15940
15941         /**
15942          * Removes the supplied dd instance from the supplied group. Executed
15943          * by DragDrop.removeFromGroup, so don't call this function directly.
15944          * @method removeDDFromGroup
15945          * @private
15946          * @static
15947          */
15948         removeDDFromGroup: function(oDD, sGroup) {
15949             if (!this.ids[sGroup]) {
15950                 this.ids[sGroup] = {};
15951             }
15952
15953             var obj = this.ids[sGroup];
15954             if (obj && obj[oDD.id]) {
15955                 delete obj[oDD.id];
15956             }
15957         },
15958
15959         /**
15960          * Unregisters a drag and drop item.  This is executed in
15961          * DragDrop.unreg, use that method instead of calling this directly.
15962          * @method _remove
15963          * @private
15964          * @static
15965          */
15966         _remove: function(oDD) {
15967             for (var g in oDD.groups) {
15968                 if (g && this.ids[g][oDD.id]) {
15969                     delete this.ids[g][oDD.id];
15970                 }
15971             }
15972             delete this.handleIds[oDD.id];
15973         },
15974
15975         /**
15976          * Each DragDrop handle element must be registered.  This is done
15977          * automatically when executing DragDrop.setHandleElId()
15978          * @method regHandle
15979          * @param {String} sDDId the DragDrop id this element is a handle for
15980          * @param {String} sHandleId the id of the element that is the drag
15981          * handle
15982          * @static
15983          */
15984         regHandle: function(sDDId, sHandleId) {
15985             if (!this.handleIds[sDDId]) {
15986                 this.handleIds[sDDId] = {};
15987             }
15988             this.handleIds[sDDId][sHandleId] = sHandleId;
15989         },
15990
15991         /**
15992          * Utility function to determine if a given element has been
15993          * registered as a drag drop item.
15994          * @method isDragDrop
15995          * @param {String} id the element id to check
15996          * @return {boolean} true if this element is a DragDrop item,
15997          * false otherwise
15998          * @static
15999          */
16000         isDragDrop: function(id) {
16001             return ( this.getDDById(id) ) ? true : false;
16002         },
16003
16004         /**
16005          * Returns the drag and drop instances that are in all groups the
16006          * passed in instance belongs to.
16007          * @method getRelated
16008          * @param {DragDrop} p_oDD the obj to get related data for
16009          * @param {boolean} bTargetsOnly if true, only return targetable objs
16010          * @return {DragDrop[]} the related instances
16011          * @static
16012          */
16013         getRelated: function(p_oDD, bTargetsOnly) {
16014             var oDDs = [];
16015             for (var i in p_oDD.groups) {
16016                 for (j in this.ids[i]) {
16017                     var dd = this.ids[i][j];
16018                     if (! this.isTypeOfDD(dd)) {
16019                         continue;
16020                     }
16021                     if (!bTargetsOnly || dd.isTarget) {
16022                         oDDs[oDDs.length] = dd;
16023                     }
16024                 }
16025             }
16026
16027             return oDDs;
16028         },
16029
16030         /**
16031          * Returns true if the specified dd target is a legal target for
16032          * the specifice drag obj
16033          * @method isLegalTarget
16034          * @param {DragDrop} the drag obj
16035          * @param {DragDrop} the target
16036          * @return {boolean} true if the target is a legal target for the
16037          * dd obj
16038          * @static
16039          */
16040         isLegalTarget: function (oDD, oTargetDD) {
16041             var targets = this.getRelated(oDD, true);
16042             for (var i=0, len=targets.length;i<len;++i) {
16043                 if (targets[i].id == oTargetDD.id) {
16044                     return true;
16045                 }
16046             }
16047
16048             return false;
16049         },
16050
16051         /**
16052          * My goal is to be able to transparently determine if an object is
16053          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16054          * returns "object", oDD.constructor.toString() always returns
16055          * "DragDrop" and not the name of the subclass.  So for now it just
16056          * evaluates a well-known variable in DragDrop.
16057          * @method isTypeOfDD
16058          * @param {Object} the object to evaluate
16059          * @return {boolean} true if typeof oDD = DragDrop
16060          * @static
16061          */
16062         isTypeOfDD: function (oDD) {
16063             return (oDD && oDD.__ygDragDrop);
16064         },
16065
16066         /**
16067          * Utility function to determine if a given element has been
16068          * registered as a drag drop handle for the given Drag Drop object.
16069          * @method isHandle
16070          * @param {String} id the element id to check
16071          * @return {boolean} true if this element is a DragDrop handle, false
16072          * otherwise
16073          * @static
16074          */
16075         isHandle: function(sDDId, sHandleId) {
16076             return ( this.handleIds[sDDId] &&
16077                             this.handleIds[sDDId][sHandleId] );
16078         },
16079
16080         /**
16081          * Returns the DragDrop instance for a given id
16082          * @method getDDById
16083          * @param {String} id the id of the DragDrop object
16084          * @return {DragDrop} the drag drop object, null if it is not found
16085          * @static
16086          */
16087         getDDById: function(id) {
16088             for (var i in this.ids) {
16089                 if (this.ids[i][id]) {
16090                     return this.ids[i][id];
16091                 }
16092             }
16093             return null;
16094         },
16095
16096         /**
16097          * Fired after a registered DragDrop object gets the mousedown event.
16098          * Sets up the events required to track the object being dragged
16099          * @method handleMouseDown
16100          * @param {Event} e the event
16101          * @param oDD the DragDrop object being dragged
16102          * @private
16103          * @static
16104          */
16105         handleMouseDown: function(e, oDD) {
16106             if(Roo.QuickTips){
16107                 Roo.QuickTips.disable();
16108             }
16109             this.currentTarget = e.getTarget();
16110
16111             this.dragCurrent = oDD;
16112
16113             var el = oDD.getEl();
16114
16115             // track start position
16116             this.startX = e.getPageX();
16117             this.startY = e.getPageY();
16118
16119             this.deltaX = this.startX - el.offsetLeft;
16120             this.deltaY = this.startY - el.offsetTop;
16121
16122             this.dragThreshMet = false;
16123
16124             this.clickTimeout = setTimeout(
16125                     function() {
16126                         var DDM = Roo.dd.DDM;
16127                         DDM.startDrag(DDM.startX, DDM.startY);
16128                     },
16129                     this.clickTimeThresh );
16130         },
16131
16132         /**
16133          * Fired when either the drag pixel threshol or the mousedown hold
16134          * time threshold has been met.
16135          * @method startDrag
16136          * @param x {int} the X position of the original mousedown
16137          * @param y {int} the Y position of the original mousedown
16138          * @static
16139          */
16140         startDrag: function(x, y) {
16141             clearTimeout(this.clickTimeout);
16142             if (this.dragCurrent) {
16143                 this.dragCurrent.b4StartDrag(x, y);
16144                 this.dragCurrent.startDrag(x, y);
16145             }
16146             this.dragThreshMet = true;
16147         },
16148
16149         /**
16150          * Internal function to handle the mouseup event.  Will be invoked
16151          * from the context of the document.
16152          * @method handleMouseUp
16153          * @param {Event} e the event
16154          * @private
16155          * @static
16156          */
16157         handleMouseUp: function(e) {
16158
16159             if(Roo.QuickTips){
16160                 Roo.QuickTips.enable();
16161             }
16162             if (! this.dragCurrent) {
16163                 return;
16164             }
16165
16166             clearTimeout(this.clickTimeout);
16167
16168             if (this.dragThreshMet) {
16169                 this.fireEvents(e, true);
16170             } else {
16171             }
16172
16173             this.stopDrag(e);
16174
16175             this.stopEvent(e);
16176         },
16177
16178         /**
16179          * Utility to stop event propagation and event default, if these
16180          * features are turned on.
16181          * @method stopEvent
16182          * @param {Event} e the event as returned by this.getEvent()
16183          * @static
16184          */
16185         stopEvent: function(e){
16186             if(this.stopPropagation) {
16187                 e.stopPropagation();
16188             }
16189
16190             if (this.preventDefault) {
16191                 e.preventDefault();
16192             }
16193         },
16194
16195         /**
16196          * Internal function to clean up event handlers after the drag
16197          * operation is complete
16198          * @method stopDrag
16199          * @param {Event} e the event
16200          * @private
16201          * @static
16202          */
16203         stopDrag: function(e) {
16204             // Fire the drag end event for the item that was dragged
16205             if (this.dragCurrent) {
16206                 if (this.dragThreshMet) {
16207                     this.dragCurrent.b4EndDrag(e);
16208                     this.dragCurrent.endDrag(e);
16209                 }
16210
16211                 this.dragCurrent.onMouseUp(e);
16212             }
16213
16214             this.dragCurrent = null;
16215             this.dragOvers = {};
16216         },
16217
16218         /**
16219          * Internal function to handle the mousemove event.  Will be invoked
16220          * from the context of the html element.
16221          *
16222          * @TODO figure out what we can do about mouse events lost when the
16223          * user drags objects beyond the window boundary.  Currently we can
16224          * detect this in internet explorer by verifying that the mouse is
16225          * down during the mousemove event.  Firefox doesn't give us the
16226          * button state on the mousemove event.
16227          * @method handleMouseMove
16228          * @param {Event} e the event
16229          * @private
16230          * @static
16231          */
16232         handleMouseMove: function(e) {
16233             if (! this.dragCurrent) {
16234                 return true;
16235             }
16236
16237             // var button = e.which || e.button;
16238
16239             // check for IE mouseup outside of page boundary
16240             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16241                 this.stopEvent(e);
16242                 return this.handleMouseUp(e);
16243             }
16244
16245             if (!this.dragThreshMet) {
16246                 var diffX = Math.abs(this.startX - e.getPageX());
16247                 var diffY = Math.abs(this.startY - e.getPageY());
16248                 if (diffX > this.clickPixelThresh ||
16249                             diffY > this.clickPixelThresh) {
16250                     this.startDrag(this.startX, this.startY);
16251                 }
16252             }
16253
16254             if (this.dragThreshMet) {
16255                 this.dragCurrent.b4Drag(e);
16256                 this.dragCurrent.onDrag(e);
16257                 if(!this.dragCurrent.moveOnly){
16258                     this.fireEvents(e, false);
16259                 }
16260             }
16261
16262             this.stopEvent(e);
16263
16264             return true;
16265         },
16266
16267         /**
16268          * Iterates over all of the DragDrop elements to find ones we are
16269          * hovering over or dropping on
16270          * @method fireEvents
16271          * @param {Event} e the event
16272          * @param {boolean} isDrop is this a drop op or a mouseover op?
16273          * @private
16274          * @static
16275          */
16276         fireEvents: function(e, isDrop) {
16277             var dc = this.dragCurrent;
16278
16279             // If the user did the mouse up outside of the window, we could
16280             // get here even though we have ended the drag.
16281             if (!dc || dc.isLocked()) {
16282                 return;
16283             }
16284
16285             var pt = e.getPoint();
16286
16287             // cache the previous dragOver array
16288             var oldOvers = [];
16289
16290             var outEvts   = [];
16291             var overEvts  = [];
16292             var dropEvts  = [];
16293             var enterEvts = [];
16294
16295             // Check to see if the object(s) we were hovering over is no longer
16296             // being hovered over so we can fire the onDragOut event
16297             for (var i in this.dragOvers) {
16298
16299                 var ddo = this.dragOvers[i];
16300
16301                 if (! this.isTypeOfDD(ddo)) {
16302                     continue;
16303                 }
16304
16305                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16306                     outEvts.push( ddo );
16307                 }
16308
16309                 oldOvers[i] = true;
16310                 delete this.dragOvers[i];
16311             }
16312
16313             for (var sGroup in dc.groups) {
16314
16315                 if ("string" != typeof sGroup) {
16316                     continue;
16317                 }
16318
16319                 for (i in this.ids[sGroup]) {
16320                     var oDD = this.ids[sGroup][i];
16321                     if (! this.isTypeOfDD(oDD)) {
16322                         continue;
16323                     }
16324
16325                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16326                         if (this.isOverTarget(pt, oDD, this.mode)) {
16327                             // look for drop interactions
16328                             if (isDrop) {
16329                                 dropEvts.push( oDD );
16330                             // look for drag enter and drag over interactions
16331                             } else {
16332
16333                                 // initial drag over: dragEnter fires
16334                                 if (!oldOvers[oDD.id]) {
16335                                     enterEvts.push( oDD );
16336                                 // subsequent drag overs: dragOver fires
16337                                 } else {
16338                                     overEvts.push( oDD );
16339                                 }
16340
16341                                 this.dragOvers[oDD.id] = oDD;
16342                             }
16343                         }
16344                     }
16345                 }
16346             }
16347
16348             if (this.mode) {
16349                 if (outEvts.length) {
16350                     dc.b4DragOut(e, outEvts);
16351                     dc.onDragOut(e, outEvts);
16352                 }
16353
16354                 if (enterEvts.length) {
16355                     dc.onDragEnter(e, enterEvts);
16356                 }
16357
16358                 if (overEvts.length) {
16359                     dc.b4DragOver(e, overEvts);
16360                     dc.onDragOver(e, overEvts);
16361                 }
16362
16363                 if (dropEvts.length) {
16364                     dc.b4DragDrop(e, dropEvts);
16365                     dc.onDragDrop(e, dropEvts);
16366                 }
16367
16368             } else {
16369                 // fire dragout events
16370                 var len = 0;
16371                 for (i=0, len=outEvts.length; i<len; ++i) {
16372                     dc.b4DragOut(e, outEvts[i].id);
16373                     dc.onDragOut(e, outEvts[i].id);
16374                 }
16375
16376                 // fire enter events
16377                 for (i=0,len=enterEvts.length; i<len; ++i) {
16378                     // dc.b4DragEnter(e, oDD.id);
16379                     dc.onDragEnter(e, enterEvts[i].id);
16380                 }
16381
16382                 // fire over events
16383                 for (i=0,len=overEvts.length; i<len; ++i) {
16384                     dc.b4DragOver(e, overEvts[i].id);
16385                     dc.onDragOver(e, overEvts[i].id);
16386                 }
16387
16388                 // fire drop events
16389                 for (i=0, len=dropEvts.length; i<len; ++i) {
16390                     dc.b4DragDrop(e, dropEvts[i].id);
16391                     dc.onDragDrop(e, dropEvts[i].id);
16392                 }
16393
16394             }
16395
16396             // notify about a drop that did not find a target
16397             if (isDrop && !dropEvts.length) {
16398                 dc.onInvalidDrop(e);
16399             }
16400
16401         },
16402
16403         /**
16404          * Helper function for getting the best match from the list of drag
16405          * and drop objects returned by the drag and drop events when we are
16406          * in INTERSECT mode.  It returns either the first object that the
16407          * cursor is over, or the object that has the greatest overlap with
16408          * the dragged element.
16409          * @method getBestMatch
16410          * @param  {DragDrop[]} dds The array of drag and drop objects
16411          * targeted
16412          * @return {DragDrop}       The best single match
16413          * @static
16414          */
16415         getBestMatch: function(dds) {
16416             var winner = null;
16417             // Return null if the input is not what we expect
16418             //if (!dds || !dds.length || dds.length == 0) {
16419                // winner = null;
16420             // If there is only one item, it wins
16421             //} else if (dds.length == 1) {
16422
16423             var len = dds.length;
16424
16425             if (len == 1) {
16426                 winner = dds[0];
16427             } else {
16428                 // Loop through the targeted items
16429                 for (var i=0; i<len; ++i) {
16430                     var dd = dds[i];
16431                     // If the cursor is over the object, it wins.  If the
16432                     // cursor is over multiple matches, the first one we come
16433                     // to wins.
16434                     if (dd.cursorIsOver) {
16435                         winner = dd;
16436                         break;
16437                     // Otherwise the object with the most overlap wins
16438                     } else {
16439                         if (!winner ||
16440                             winner.overlap.getArea() < dd.overlap.getArea()) {
16441                             winner = dd;
16442                         }
16443                     }
16444                 }
16445             }
16446
16447             return winner;
16448         },
16449
16450         /**
16451          * Refreshes the cache of the top-left and bottom-right points of the
16452          * drag and drop objects in the specified group(s).  This is in the
16453          * format that is stored in the drag and drop instance, so typical
16454          * usage is:
16455          * <code>
16456          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16457          * </code>
16458          * Alternatively:
16459          * <code>
16460          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16461          * </code>
16462          * @TODO this really should be an indexed array.  Alternatively this
16463          * method could accept both.
16464          * @method refreshCache
16465          * @param {Object} groups an associative array of groups to refresh
16466          * @static
16467          */
16468         refreshCache: function(groups) {
16469             for (var sGroup in groups) {
16470                 if ("string" != typeof sGroup) {
16471                     continue;
16472                 }
16473                 for (var i in this.ids[sGroup]) {
16474                     var oDD = this.ids[sGroup][i];
16475
16476                     if (this.isTypeOfDD(oDD)) {
16477                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16478                         var loc = this.getLocation(oDD);
16479                         if (loc) {
16480                             this.locationCache[oDD.id] = loc;
16481                         } else {
16482                             delete this.locationCache[oDD.id];
16483                             // this will unregister the drag and drop object if
16484                             // the element is not in a usable state
16485                             // oDD.unreg();
16486                         }
16487                     }
16488                 }
16489             }
16490         },
16491
16492         /**
16493          * This checks to make sure an element exists and is in the DOM.  The
16494          * main purpose is to handle cases where innerHTML is used to remove
16495          * drag and drop objects from the DOM.  IE provides an 'unspecified
16496          * error' when trying to access the offsetParent of such an element
16497          * @method verifyEl
16498          * @param {HTMLElement} el the element to check
16499          * @return {boolean} true if the element looks usable
16500          * @static
16501          */
16502         verifyEl: function(el) {
16503             if (el) {
16504                 var parent;
16505                 if(Roo.isIE){
16506                     try{
16507                         parent = el.offsetParent;
16508                     }catch(e){}
16509                 }else{
16510                     parent = el.offsetParent;
16511                 }
16512                 if (parent) {
16513                     return true;
16514                 }
16515             }
16516
16517             return false;
16518         },
16519
16520         /**
16521          * Returns a Region object containing the drag and drop element's position
16522          * and size, including the padding configured for it
16523          * @method getLocation
16524          * @param {DragDrop} oDD the drag and drop object to get the
16525          *                       location for
16526          * @return {Roo.lib.Region} a Region object representing the total area
16527          *                             the element occupies, including any padding
16528          *                             the instance is configured for.
16529          * @static
16530          */
16531         getLocation: function(oDD) {
16532             if (! this.isTypeOfDD(oDD)) {
16533                 return null;
16534             }
16535
16536             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16537
16538             try {
16539                 pos= Roo.lib.Dom.getXY(el);
16540             } catch (e) { }
16541
16542             if (!pos) {
16543                 return null;
16544             }
16545
16546             x1 = pos[0];
16547             x2 = x1 + el.offsetWidth;
16548             y1 = pos[1];
16549             y2 = y1 + el.offsetHeight;
16550
16551             t = y1 - oDD.padding[0];
16552             r = x2 + oDD.padding[1];
16553             b = y2 + oDD.padding[2];
16554             l = x1 - oDD.padding[3];
16555
16556             return new Roo.lib.Region( t, r, b, l );
16557         },
16558
16559         /**
16560          * Checks the cursor location to see if it over the target
16561          * @method isOverTarget
16562          * @param {Roo.lib.Point} pt The point to evaluate
16563          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16564          * @return {boolean} true if the mouse is over the target
16565          * @private
16566          * @static
16567          */
16568         isOverTarget: function(pt, oTarget, intersect) {
16569             // use cache if available
16570             var loc = this.locationCache[oTarget.id];
16571             if (!loc || !this.useCache) {
16572                 loc = this.getLocation(oTarget);
16573                 this.locationCache[oTarget.id] = loc;
16574
16575             }
16576
16577             if (!loc) {
16578                 return false;
16579             }
16580
16581             oTarget.cursorIsOver = loc.contains( pt );
16582
16583             // DragDrop is using this as a sanity check for the initial mousedown
16584             // in this case we are done.  In POINT mode, if the drag obj has no
16585             // contraints, we are also done. Otherwise we need to evaluate the
16586             // location of the target as related to the actual location of the
16587             // dragged element.
16588             var dc = this.dragCurrent;
16589             if (!dc || !dc.getTargetCoord ||
16590                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16591                 return oTarget.cursorIsOver;
16592             }
16593
16594             oTarget.overlap = null;
16595
16596             // Get the current location of the drag element, this is the
16597             // location of the mouse event less the delta that represents
16598             // where the original mousedown happened on the element.  We
16599             // need to consider constraints and ticks as well.
16600             var pos = dc.getTargetCoord(pt.x, pt.y);
16601
16602             var el = dc.getDragEl();
16603             var curRegion = new Roo.lib.Region( pos.y,
16604                                                    pos.x + el.offsetWidth,
16605                                                    pos.y + el.offsetHeight,
16606                                                    pos.x );
16607
16608             var overlap = curRegion.intersect(loc);
16609
16610             if (overlap) {
16611                 oTarget.overlap = overlap;
16612                 return (intersect) ? true : oTarget.cursorIsOver;
16613             } else {
16614                 return false;
16615             }
16616         },
16617
16618         /**
16619          * unload event handler
16620          * @method _onUnload
16621          * @private
16622          * @static
16623          */
16624         _onUnload: function(e, me) {
16625             Roo.dd.DragDropMgr.unregAll();
16626         },
16627
16628         /**
16629          * Cleans up the drag and drop events and objects.
16630          * @method unregAll
16631          * @private
16632          * @static
16633          */
16634         unregAll: function() {
16635
16636             if (this.dragCurrent) {
16637                 this.stopDrag();
16638                 this.dragCurrent = null;
16639             }
16640
16641             this._execOnAll("unreg", []);
16642
16643             for (i in this.elementCache) {
16644                 delete this.elementCache[i];
16645             }
16646
16647             this.elementCache = {};
16648             this.ids = {};
16649         },
16650
16651         /**
16652          * A cache of DOM elements
16653          * @property elementCache
16654          * @private
16655          * @static
16656          */
16657         elementCache: {},
16658
16659         /**
16660          * Get the wrapper for the DOM element specified
16661          * @method getElWrapper
16662          * @param {String} id the id of the element to get
16663          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16664          * @private
16665          * @deprecated This wrapper isn't that useful
16666          * @static
16667          */
16668         getElWrapper: function(id) {
16669             var oWrapper = this.elementCache[id];
16670             if (!oWrapper || !oWrapper.el) {
16671                 oWrapper = this.elementCache[id] =
16672                     new this.ElementWrapper(Roo.getDom(id));
16673             }
16674             return oWrapper;
16675         },
16676
16677         /**
16678          * Returns the actual DOM element
16679          * @method getElement
16680          * @param {String} id the id of the elment to get
16681          * @return {Object} The element
16682          * @deprecated use Roo.getDom instead
16683          * @static
16684          */
16685         getElement: function(id) {
16686             return Roo.getDom(id);
16687         },
16688
16689         /**
16690          * Returns the style property for the DOM element (i.e.,
16691          * document.getElById(id).style)
16692          * @method getCss
16693          * @param {String} id the id of the elment to get
16694          * @return {Object} The style property of the element
16695          * @deprecated use Roo.getDom instead
16696          * @static
16697          */
16698         getCss: function(id) {
16699             var el = Roo.getDom(id);
16700             return (el) ? el.style : null;
16701         },
16702
16703         /**
16704          * Inner class for cached elements
16705          * @class DragDropMgr.ElementWrapper
16706          * @for DragDropMgr
16707          * @private
16708          * @deprecated
16709          */
16710         ElementWrapper: function(el) {
16711                 /**
16712                  * The element
16713                  * @property el
16714                  */
16715                 this.el = el || null;
16716                 /**
16717                  * The element id
16718                  * @property id
16719                  */
16720                 this.id = this.el && el.id;
16721                 /**
16722                  * A reference to the style property
16723                  * @property css
16724                  */
16725                 this.css = this.el && el.style;
16726             },
16727
16728         /**
16729          * Returns the X position of an html element
16730          * @method getPosX
16731          * @param el the element for which to get the position
16732          * @return {int} the X coordinate
16733          * @for DragDropMgr
16734          * @deprecated use Roo.lib.Dom.getX instead
16735          * @static
16736          */
16737         getPosX: function(el) {
16738             return Roo.lib.Dom.getX(el);
16739         },
16740
16741         /**
16742          * Returns the Y position of an html element
16743          * @method getPosY
16744          * @param el the element for which to get the position
16745          * @return {int} the Y coordinate
16746          * @deprecated use Roo.lib.Dom.getY instead
16747          * @static
16748          */
16749         getPosY: function(el) {
16750             return Roo.lib.Dom.getY(el);
16751         },
16752
16753         /**
16754          * Swap two nodes.  In IE, we use the native method, for others we
16755          * emulate the IE behavior
16756          * @method swapNode
16757          * @param n1 the first node to swap
16758          * @param n2 the other node to swap
16759          * @static
16760          */
16761         swapNode: function(n1, n2) {
16762             if (n1.swapNode) {
16763                 n1.swapNode(n2);
16764             } else {
16765                 var p = n2.parentNode;
16766                 var s = n2.nextSibling;
16767
16768                 if (s == n1) {
16769                     p.insertBefore(n1, n2);
16770                 } else if (n2 == n1.nextSibling) {
16771                     p.insertBefore(n2, n1);
16772                 } else {
16773                     n1.parentNode.replaceChild(n2, n1);
16774                     p.insertBefore(n1, s);
16775                 }
16776             }
16777         },
16778
16779         /**
16780          * Returns the current scroll position
16781          * @method getScroll
16782          * @private
16783          * @static
16784          */
16785         getScroll: function () {
16786             var t, l, dde=document.documentElement, db=document.body;
16787             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16788                 t = dde.scrollTop;
16789                 l = dde.scrollLeft;
16790             } else if (db) {
16791                 t = db.scrollTop;
16792                 l = db.scrollLeft;
16793             } else {
16794
16795             }
16796             return { top: t, left: l };
16797         },
16798
16799         /**
16800          * Returns the specified element style property
16801          * @method getStyle
16802          * @param {HTMLElement} el          the element
16803          * @param {string}      styleProp   the style property
16804          * @return {string} The value of the style property
16805          * @deprecated use Roo.lib.Dom.getStyle
16806          * @static
16807          */
16808         getStyle: function(el, styleProp) {
16809             return Roo.fly(el).getStyle(styleProp);
16810         },
16811
16812         /**
16813          * Gets the scrollTop
16814          * @method getScrollTop
16815          * @return {int} the document's scrollTop
16816          * @static
16817          */
16818         getScrollTop: function () { return this.getScroll().top; },
16819
16820         /**
16821          * Gets the scrollLeft
16822          * @method getScrollLeft
16823          * @return {int} the document's scrollTop
16824          * @static
16825          */
16826         getScrollLeft: function () { return this.getScroll().left; },
16827
16828         /**
16829          * Sets the x/y position of an element to the location of the
16830          * target element.
16831          * @method moveToEl
16832          * @param {HTMLElement} moveEl      The element to move
16833          * @param {HTMLElement} targetEl    The position reference element
16834          * @static
16835          */
16836         moveToEl: function (moveEl, targetEl) {
16837             var aCoord = Roo.lib.Dom.getXY(targetEl);
16838             Roo.lib.Dom.setXY(moveEl, aCoord);
16839         },
16840
16841         /**
16842          * Numeric array sort function
16843          * @method numericSort
16844          * @static
16845          */
16846         numericSort: function(a, b) { return (a - b); },
16847
16848         /**
16849          * Internal counter
16850          * @property _timeoutCount
16851          * @private
16852          * @static
16853          */
16854         _timeoutCount: 0,
16855
16856         /**
16857          * Trying to make the load order less important.  Without this we get
16858          * an error if this file is loaded before the Event Utility.
16859          * @method _addListeners
16860          * @private
16861          * @static
16862          */
16863         _addListeners: function() {
16864             var DDM = Roo.dd.DDM;
16865             if ( Roo.lib.Event && document ) {
16866                 DDM._onLoad();
16867             } else {
16868                 if (DDM._timeoutCount > 2000) {
16869                 } else {
16870                     setTimeout(DDM._addListeners, 10);
16871                     if (document && document.body) {
16872                         DDM._timeoutCount += 1;
16873                     }
16874                 }
16875             }
16876         },
16877
16878         /**
16879          * Recursively searches the immediate parent and all child nodes for
16880          * the handle element in order to determine wheter or not it was
16881          * clicked.
16882          * @method handleWasClicked
16883          * @param node the html element to inspect
16884          * @static
16885          */
16886         handleWasClicked: function(node, id) {
16887             if (this.isHandle(id, node.id)) {
16888                 return true;
16889             } else {
16890                 // check to see if this is a text node child of the one we want
16891                 var p = node.parentNode;
16892
16893                 while (p) {
16894                     if (this.isHandle(id, p.id)) {
16895                         return true;
16896                     } else {
16897                         p = p.parentNode;
16898                     }
16899                 }
16900             }
16901
16902             return false;
16903         }
16904
16905     };
16906
16907 }();
16908
16909 // shorter alias, save a few bytes
16910 Roo.dd.DDM = Roo.dd.DragDropMgr;
16911 Roo.dd.DDM._addListeners();
16912
16913 }/*
16914  * Based on:
16915  * Ext JS Library 1.1.1
16916  * Copyright(c) 2006-2007, Ext JS, LLC.
16917  *
16918  * Originally Released Under LGPL - original licence link has changed is not relivant.
16919  *
16920  * Fork - LGPL
16921  * <script type="text/javascript">
16922  */
16923
16924 /**
16925  * @class Roo.dd.DD
16926  * A DragDrop implementation where the linked element follows the
16927  * mouse cursor during a drag.
16928  * @extends Roo.dd.DragDrop
16929  * @constructor
16930  * @param {String} id the id of the linked element
16931  * @param {String} sGroup the group of related DragDrop items
16932  * @param {object} config an object containing configurable attributes
16933  *                Valid properties for DD:
16934  *                    scroll
16935  */
16936 Roo.dd.DD = function(id, sGroup, config) {
16937     if (id) {
16938         this.init(id, sGroup, config);
16939     }
16940 };
16941
16942 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16943
16944     /**
16945      * When set to true, the utility automatically tries to scroll the browser
16946      * window wehn a drag and drop element is dragged near the viewport boundary.
16947      * Defaults to true.
16948      * @property scroll
16949      * @type boolean
16950      */
16951     scroll: true,
16952
16953     /**
16954      * Sets the pointer offset to the distance between the linked element's top
16955      * left corner and the location the element was clicked
16956      * @method autoOffset
16957      * @param {int} iPageX the X coordinate of the click
16958      * @param {int} iPageY the Y coordinate of the click
16959      */
16960     autoOffset: function(iPageX, iPageY) {
16961         var x = iPageX - this.startPageX;
16962         var y = iPageY - this.startPageY;
16963         this.setDelta(x, y);
16964     },
16965
16966     /**
16967      * Sets the pointer offset.  You can call this directly to force the
16968      * offset to be in a particular location (e.g., pass in 0,0 to set it
16969      * to the center of the object)
16970      * @method setDelta
16971      * @param {int} iDeltaX the distance from the left
16972      * @param {int} iDeltaY the distance from the top
16973      */
16974     setDelta: function(iDeltaX, iDeltaY) {
16975         this.deltaX = iDeltaX;
16976         this.deltaY = iDeltaY;
16977     },
16978
16979     /**
16980      * Sets the drag element to the location of the mousedown or click event,
16981      * maintaining the cursor location relative to the location on the element
16982      * that was clicked.  Override this if you want to place the element in a
16983      * location other than where the cursor is.
16984      * @method setDragElPos
16985      * @param {int} iPageX the X coordinate of the mousedown or drag event
16986      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16987      */
16988     setDragElPos: function(iPageX, iPageY) {
16989         // the first time we do this, we are going to check to make sure
16990         // the element has css positioning
16991
16992         var el = this.getDragEl();
16993         this.alignElWithMouse(el, iPageX, iPageY);
16994     },
16995
16996     /**
16997      * Sets the element to the location of the mousedown or click event,
16998      * maintaining the cursor location relative to the location on the element
16999      * that was clicked.  Override this if you want to place the element in a
17000      * location other than where the cursor is.
17001      * @method alignElWithMouse
17002      * @param {HTMLElement} el the element to move
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     alignElWithMouse: function(el, iPageX, iPageY) {
17007         var oCoord = this.getTargetCoord(iPageX, iPageY);
17008         var fly = el.dom ? el : Roo.fly(el);
17009         if (!this.deltaSetXY) {
17010             var aCoord = [oCoord.x, oCoord.y];
17011             fly.setXY(aCoord);
17012             var newLeft = fly.getLeft(true);
17013             var newTop  = fly.getTop(true);
17014             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17015         } else {
17016             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17017         }
17018
17019         this.cachePosition(oCoord.x, oCoord.y);
17020         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17021         return oCoord;
17022     },
17023
17024     /**
17025      * Saves the most recent position so that we can reset the constraints and
17026      * tick marks on-demand.  We need to know this so that we can calculate the
17027      * number of pixels the element is offset from its original position.
17028      * @method cachePosition
17029      * @param iPageX the current x position (optional, this just makes it so we
17030      * don't have to look it up again)
17031      * @param iPageY the current y position (optional, this just makes it so we
17032      * don't have to look it up again)
17033      */
17034     cachePosition: function(iPageX, iPageY) {
17035         if (iPageX) {
17036             this.lastPageX = iPageX;
17037             this.lastPageY = iPageY;
17038         } else {
17039             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17040             this.lastPageX = aCoord[0];
17041             this.lastPageY = aCoord[1];
17042         }
17043     },
17044
17045     /**
17046      * Auto-scroll the window if the dragged object has been moved beyond the
17047      * visible window boundary.
17048      * @method autoScroll
17049      * @param {int} x the drag element's x position
17050      * @param {int} y the drag element's y position
17051      * @param {int} h the height of the drag element
17052      * @param {int} w the width of the drag element
17053      * @private
17054      */
17055     autoScroll: function(x, y, h, w) {
17056
17057         if (this.scroll) {
17058             // The client height
17059             var clientH = Roo.lib.Dom.getViewWidth();
17060
17061             // The client width
17062             var clientW = Roo.lib.Dom.getViewHeight();
17063
17064             // The amt scrolled down
17065             var st = this.DDM.getScrollTop();
17066
17067             // The amt scrolled right
17068             var sl = this.DDM.getScrollLeft();
17069
17070             // Location of the bottom of the element
17071             var bot = h + y;
17072
17073             // Location of the right of the element
17074             var right = w + x;
17075
17076             // The distance from the cursor to the bottom of the visible area,
17077             // adjusted so that we don't scroll if the cursor is beyond the
17078             // element drag constraints
17079             var toBot = (clientH + st - y - this.deltaY);
17080
17081             // The distance from the cursor to the right of the visible area
17082             var toRight = (clientW + sl - x - this.deltaX);
17083
17084
17085             // How close to the edge the cursor must be before we scroll
17086             // var thresh = (document.all) ? 100 : 40;
17087             var thresh = 40;
17088
17089             // How many pixels to scroll per autoscroll op.  This helps to reduce
17090             // clunky scrolling. IE is more sensitive about this ... it needs this
17091             // value to be higher.
17092             var scrAmt = (document.all) ? 80 : 30;
17093
17094             // Scroll down if we are near the bottom of the visible page and the
17095             // obj extends below the crease
17096             if ( bot > clientH && toBot < thresh ) {
17097                 window.scrollTo(sl, st + scrAmt);
17098             }
17099
17100             // Scroll up if the window is scrolled down and the top of the object
17101             // goes above the top border
17102             if ( y < st && st > 0 && y - st < thresh ) {
17103                 window.scrollTo(sl, st - scrAmt);
17104             }
17105
17106             // Scroll right if the obj is beyond the right border and the cursor is
17107             // near the border.
17108             if ( right > clientW && toRight < thresh ) {
17109                 window.scrollTo(sl + scrAmt, st);
17110             }
17111
17112             // Scroll left if the window has been scrolled to the right and the obj
17113             // extends past the left border
17114             if ( x < sl && sl > 0 && x - sl < thresh ) {
17115                 window.scrollTo(sl - scrAmt, st);
17116             }
17117         }
17118     },
17119
17120     /**
17121      * Finds the location the element should be placed if we want to move
17122      * it to where the mouse location less the click offset would place us.
17123      * @method getTargetCoord
17124      * @param {int} iPageX the X coordinate of the click
17125      * @param {int} iPageY the Y coordinate of the click
17126      * @return an object that contains the coordinates (Object.x and Object.y)
17127      * @private
17128      */
17129     getTargetCoord: function(iPageX, iPageY) {
17130
17131
17132         var x = iPageX - this.deltaX;
17133         var y = iPageY - this.deltaY;
17134
17135         if (this.constrainX) {
17136             if (x < this.minX) { x = this.minX; }
17137             if (x > this.maxX) { x = this.maxX; }
17138         }
17139
17140         if (this.constrainY) {
17141             if (y < this.minY) { y = this.minY; }
17142             if (y > this.maxY) { y = this.maxY; }
17143         }
17144
17145         x = this.getTick(x, this.xTicks);
17146         y = this.getTick(y, this.yTicks);
17147
17148
17149         return {x:x, y:y};
17150     },
17151
17152     /*
17153      * Sets up config options specific to this class. Overrides
17154      * Roo.dd.DragDrop, but all versions of this method through the
17155      * inheritance chain are called
17156      */
17157     applyConfig: function() {
17158         Roo.dd.DD.superclass.applyConfig.call(this);
17159         this.scroll = (this.config.scroll !== false);
17160     },
17161
17162     /*
17163      * Event that fires prior to the onMouseDown event.  Overrides
17164      * Roo.dd.DragDrop.
17165      */
17166     b4MouseDown: function(e) {
17167         // this.resetConstraints();
17168         this.autoOffset(e.getPageX(),
17169                             e.getPageY());
17170     },
17171
17172     /*
17173      * Event that fires prior to the onDrag event.  Overrides
17174      * Roo.dd.DragDrop.
17175      */
17176     b4Drag: function(e) {
17177         this.setDragElPos(e.getPageX(),
17178                             e.getPageY());
17179     },
17180
17181     toString: function() {
17182         return ("DD " + this.id);
17183     }
17184
17185     //////////////////////////////////////////////////////////////////////////
17186     // Debugging ygDragDrop events that can be overridden
17187     //////////////////////////////////////////////////////////////////////////
17188     /*
17189     startDrag: function(x, y) {
17190     },
17191
17192     onDrag: function(e) {
17193     },
17194
17195     onDragEnter: function(e, id) {
17196     },
17197
17198     onDragOver: function(e, id) {
17199     },
17200
17201     onDragOut: function(e, id) {
17202     },
17203
17204     onDragDrop: function(e, id) {
17205     },
17206
17207     endDrag: function(e) {
17208     }
17209
17210     */
17211
17212 });/*
17213  * Based on:
17214  * Ext JS Library 1.1.1
17215  * Copyright(c) 2006-2007, Ext JS, LLC.
17216  *
17217  * Originally Released Under LGPL - original licence link has changed is not relivant.
17218  *
17219  * Fork - LGPL
17220  * <script type="text/javascript">
17221  */
17222
17223 /**
17224  * @class Roo.dd.DDProxy
17225  * A DragDrop implementation that inserts an empty, bordered div into
17226  * the document that follows the cursor during drag operations.  At the time of
17227  * the click, the frame div is resized to the dimensions of the linked html
17228  * element, and moved to the exact location of the linked element.
17229  *
17230  * References to the "frame" element refer to the single proxy element that
17231  * was created to be dragged in place of all DDProxy elements on the
17232  * page.
17233  *
17234  * @extends Roo.dd.DD
17235  * @constructor
17236  * @param {String} id the id of the linked html element
17237  * @param {String} sGroup the group of related DragDrop objects
17238  * @param {object} config an object containing configurable attributes
17239  *                Valid properties for DDProxy in addition to those in DragDrop:
17240  *                   resizeFrame, centerFrame, dragElId
17241  */
17242 Roo.dd.DDProxy = function(id, sGroup, config) {
17243     if (id) {
17244         this.init(id, sGroup, config);
17245         this.initFrame();
17246     }
17247 };
17248
17249 /**
17250  * The default drag frame div id
17251  * @property Roo.dd.DDProxy.dragElId
17252  * @type String
17253  * @static
17254  */
17255 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17256
17257 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17258
17259     /**
17260      * By default we resize the drag frame to be the same size as the element
17261      * we want to drag (this is to get the frame effect).  We can turn it off
17262      * if we want a different behavior.
17263      * @property resizeFrame
17264      * @type boolean
17265      */
17266     resizeFrame: true,
17267
17268     /**
17269      * By default the frame is positioned exactly where the drag element is, so
17270      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17271      * you do not have constraints on the obj is to have the drag frame centered
17272      * around the cursor.  Set centerFrame to true for this effect.
17273      * @property centerFrame
17274      * @type boolean
17275      */
17276     centerFrame: false,
17277
17278     /**
17279      * Creates the proxy element if it does not yet exist
17280      * @method createFrame
17281      */
17282     createFrame: function() {
17283         var self = this;
17284         var body = document.body;
17285
17286         if (!body || !body.firstChild) {
17287             setTimeout( function() { self.createFrame(); }, 50 );
17288             return;
17289         }
17290
17291         var div = this.getDragEl();
17292
17293         if (!div) {
17294             div    = document.createElement("div");
17295             div.id = this.dragElId;
17296             var s  = div.style;
17297
17298             s.position   = "absolute";
17299             s.visibility = "hidden";
17300             s.cursor     = "move";
17301             s.border     = "2px solid #aaa";
17302             s.zIndex     = 999;
17303
17304             // appendChild can blow up IE if invoked prior to the window load event
17305             // while rendering a table.  It is possible there are other scenarios
17306             // that would cause this to happen as well.
17307             body.insertBefore(div, body.firstChild);
17308         }
17309     },
17310
17311     /**
17312      * Initialization for the drag frame element.  Must be called in the
17313      * constructor of all subclasses
17314      * @method initFrame
17315      */
17316     initFrame: function() {
17317         this.createFrame();
17318     },
17319
17320     applyConfig: function() {
17321         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17322
17323         this.resizeFrame = (this.config.resizeFrame !== false);
17324         this.centerFrame = (this.config.centerFrame);
17325         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17326     },
17327
17328     /**
17329      * Resizes the drag frame to the dimensions of the clicked object, positions
17330      * it over the object, and finally displays it
17331      * @method showFrame
17332      * @param {int} iPageX X click position
17333      * @param {int} iPageY Y click position
17334      * @private
17335      */
17336     showFrame: function(iPageX, iPageY) {
17337         var el = this.getEl();
17338         var dragEl = this.getDragEl();
17339         var s = dragEl.style;
17340
17341         this._resizeProxy();
17342
17343         if (this.centerFrame) {
17344             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17345                            Math.round(parseInt(s.height, 10)/2) );
17346         }
17347
17348         this.setDragElPos(iPageX, iPageY);
17349
17350         Roo.fly(dragEl).show();
17351     },
17352
17353     /**
17354      * The proxy is automatically resized to the dimensions of the linked
17355      * element when a drag is initiated, unless resizeFrame is set to false
17356      * @method _resizeProxy
17357      * @private
17358      */
17359     _resizeProxy: function() {
17360         if (this.resizeFrame) {
17361             var el = this.getEl();
17362             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17363         }
17364     },
17365
17366     // overrides Roo.dd.DragDrop
17367     b4MouseDown: function(e) {
17368         var x = e.getPageX();
17369         var y = e.getPageY();
17370         this.autoOffset(x, y);
17371         this.setDragElPos(x, y);
17372     },
17373
17374     // overrides Roo.dd.DragDrop
17375     b4StartDrag: function(x, y) {
17376         // show the drag frame
17377         this.showFrame(x, y);
17378     },
17379
17380     // overrides Roo.dd.DragDrop
17381     b4EndDrag: function(e) {
17382         Roo.fly(this.getDragEl()).hide();
17383     },
17384
17385     // overrides Roo.dd.DragDrop
17386     // By default we try to move the element to the last location of the frame.
17387     // This is so that the default behavior mirrors that of Roo.dd.DD.
17388     endDrag: function(e) {
17389
17390         var lel = this.getEl();
17391         var del = this.getDragEl();
17392
17393         // Show the drag frame briefly so we can get its position
17394         del.style.visibility = "";
17395
17396         this.beforeMove();
17397         // Hide the linked element before the move to get around a Safari
17398         // rendering bug.
17399         lel.style.visibility = "hidden";
17400         Roo.dd.DDM.moveToEl(lel, del);
17401         del.style.visibility = "hidden";
17402         lel.style.visibility = "";
17403
17404         this.afterDrag();
17405     },
17406
17407     beforeMove : function(){
17408
17409     },
17410
17411     afterDrag : function(){
17412
17413     },
17414
17415     toString: function() {
17416         return ("DDProxy " + this.id);
17417     }
17418
17419 });
17420 /*
17421  * Based on:
17422  * Ext JS Library 1.1.1
17423  * Copyright(c) 2006-2007, Ext JS, LLC.
17424  *
17425  * Originally Released Under LGPL - original licence link has changed is not relivant.
17426  *
17427  * Fork - LGPL
17428  * <script type="text/javascript">
17429  */
17430
17431  /**
17432  * @class Roo.dd.DDTarget
17433  * A DragDrop implementation that does not move, but can be a drop
17434  * target.  You would get the same result by simply omitting implementation
17435  * for the event callbacks, but this way we reduce the processing cost of the
17436  * event listener and the callbacks.
17437  * @extends Roo.dd.DragDrop
17438  * @constructor
17439  * @param {String} id the id of the element that is a drop target
17440  * @param {String} sGroup the group of related DragDrop objects
17441  * @param {object} config an object containing configurable attributes
17442  *                 Valid properties for DDTarget in addition to those in
17443  *                 DragDrop:
17444  *                    none
17445  */
17446 Roo.dd.DDTarget = function(id, sGroup, config) {
17447     if (id) {
17448         this.initTarget(id, sGroup, config);
17449     }
17450     if (config.listeners || config.events) { 
17451        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17452             listeners : config.listeners || {}, 
17453             events : config.events || {} 
17454         });    
17455     }
17456 };
17457
17458 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17459 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17460     toString: function() {
17461         return ("DDTarget " + this.id);
17462     }
17463 });
17464 /*
17465  * Based on:
17466  * Ext JS Library 1.1.1
17467  * Copyright(c) 2006-2007, Ext JS, LLC.
17468  *
17469  * Originally Released Under LGPL - original licence link has changed is not relivant.
17470  *
17471  * Fork - LGPL
17472  * <script type="text/javascript">
17473  */
17474  
17475
17476 /**
17477  * @class Roo.dd.ScrollManager
17478  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17479  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17480  * @singleton
17481  */
17482 Roo.dd.ScrollManager = function(){
17483     var ddm = Roo.dd.DragDropMgr;
17484     var els = {};
17485     var dragEl = null;
17486     var proc = {};
17487     
17488     var onStop = function(e){
17489         dragEl = null;
17490         clearProc();
17491     };
17492     
17493     var triggerRefresh = function(){
17494         if(ddm.dragCurrent){
17495              ddm.refreshCache(ddm.dragCurrent.groups);
17496         }
17497     };
17498     
17499     var doScroll = function(){
17500         if(ddm.dragCurrent){
17501             var dds = Roo.dd.ScrollManager;
17502             if(!dds.animate){
17503                 if(proc.el.scroll(proc.dir, dds.increment)){
17504                     triggerRefresh();
17505                 }
17506             }else{
17507                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17508             }
17509         }
17510     };
17511     
17512     var clearProc = function(){
17513         if(proc.id){
17514             clearInterval(proc.id);
17515         }
17516         proc.id = 0;
17517         proc.el = null;
17518         proc.dir = "";
17519     };
17520     
17521     var startProc = function(el, dir){
17522         clearProc();
17523         proc.el = el;
17524         proc.dir = dir;
17525         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17526     };
17527     
17528     var onFire = function(e, isDrop){
17529         if(isDrop || !ddm.dragCurrent){ return; }
17530         var dds = Roo.dd.ScrollManager;
17531         if(!dragEl || dragEl != ddm.dragCurrent){
17532             dragEl = ddm.dragCurrent;
17533             // refresh regions on drag start
17534             dds.refreshCache();
17535         }
17536         
17537         var xy = Roo.lib.Event.getXY(e);
17538         var pt = new Roo.lib.Point(xy[0], xy[1]);
17539         for(var id in els){
17540             var el = els[id], r = el._region;
17541             if(r && r.contains(pt) && el.isScrollable()){
17542                 if(r.bottom - pt.y <= dds.thresh){
17543                     if(proc.el != el){
17544                         startProc(el, "down");
17545                     }
17546                     return;
17547                 }else if(r.right - pt.x <= dds.thresh){
17548                     if(proc.el != el){
17549                         startProc(el, "left");
17550                     }
17551                     return;
17552                 }else if(pt.y - r.top <= dds.thresh){
17553                     if(proc.el != el){
17554                         startProc(el, "up");
17555                     }
17556                     return;
17557                 }else if(pt.x - r.left <= dds.thresh){
17558                     if(proc.el != el){
17559                         startProc(el, "right");
17560                     }
17561                     return;
17562                 }
17563             }
17564         }
17565         clearProc();
17566     };
17567     
17568     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17569     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17570     
17571     return {
17572         /**
17573          * Registers new overflow element(s) to auto scroll
17574          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17575          */
17576         register : function(el){
17577             if(el instanceof Array){
17578                 for(var i = 0, len = el.length; i < len; i++) {
17579                         this.register(el[i]);
17580                 }
17581             }else{
17582                 el = Roo.get(el);
17583                 els[el.id] = el;
17584             }
17585         },
17586         
17587         /**
17588          * Unregisters overflow element(s) so they are no longer scrolled
17589          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17590          */
17591         unregister : function(el){
17592             if(el instanceof Array){
17593                 for(var i = 0, len = el.length; i < len; i++) {
17594                         this.unregister(el[i]);
17595                 }
17596             }else{
17597                 el = Roo.get(el);
17598                 delete els[el.id];
17599             }
17600         },
17601         
17602         /**
17603          * The number of pixels from the edge of a container the pointer needs to be to 
17604          * trigger scrolling (defaults to 25)
17605          * @type Number
17606          */
17607         thresh : 25,
17608         
17609         /**
17610          * The number of pixels to scroll in each scroll increment (defaults to 50)
17611          * @type Number
17612          */
17613         increment : 100,
17614         
17615         /**
17616          * The frequency of scrolls in milliseconds (defaults to 500)
17617          * @type Number
17618          */
17619         frequency : 500,
17620         
17621         /**
17622          * True to animate the scroll (defaults to true)
17623          * @type Boolean
17624          */
17625         animate: true,
17626         
17627         /**
17628          * The animation duration in seconds - 
17629          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17630          * @type Number
17631          */
17632         animDuration: .4,
17633         
17634         /**
17635          * Manually trigger a cache refresh.
17636          */
17637         refreshCache : function(){
17638             for(var id in els){
17639                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17640                     els[id]._region = els[id].getRegion();
17641                 }
17642             }
17643         }
17644     };
17645 }();/*
17646  * Based on:
17647  * Ext JS Library 1.1.1
17648  * Copyright(c) 2006-2007, Ext JS, LLC.
17649  *
17650  * Originally Released Under LGPL - original licence link has changed is not relivant.
17651  *
17652  * Fork - LGPL
17653  * <script type="text/javascript">
17654  */
17655  
17656
17657 /**
17658  * @class Roo.dd.Registry
17659  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17660  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17661  * @singleton
17662  */
17663 Roo.dd.Registry = function(){
17664     var elements = {}; 
17665     var handles = {}; 
17666     var autoIdSeed = 0;
17667
17668     var getId = function(el, autogen){
17669         if(typeof el == "string"){
17670             return el;
17671         }
17672         var id = el.id;
17673         if(!id && autogen !== false){
17674             id = "roodd-" + (++autoIdSeed);
17675             el.id = id;
17676         }
17677         return id;
17678     };
17679     
17680     return {
17681     /**
17682      * Register a drag drop element
17683      * @param {String|HTMLElement} element The id or DOM node to register
17684      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17685      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17686      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17687      * populated in the data object (if applicable):
17688      * <pre>
17689 Value      Description<br />
17690 ---------  ------------------------------------------<br />
17691 handles    Array of DOM nodes that trigger dragging<br />
17692            for the element being registered<br />
17693 isHandle   True if the element passed in triggers<br />
17694            dragging itself, else false
17695 </pre>
17696      */
17697         register : function(el, data){
17698             data = data || {};
17699             if(typeof el == "string"){
17700                 el = document.getElementById(el);
17701             }
17702             data.ddel = el;
17703             elements[getId(el)] = data;
17704             if(data.isHandle !== false){
17705                 handles[data.ddel.id] = data;
17706             }
17707             if(data.handles){
17708                 var hs = data.handles;
17709                 for(var i = 0, len = hs.length; i < len; i++){
17710                         handles[getId(hs[i])] = data;
17711                 }
17712             }
17713         },
17714
17715     /**
17716      * Unregister a drag drop element
17717      * @param {String|HTMLElement}  element The id or DOM node to unregister
17718      */
17719         unregister : function(el){
17720             var id = getId(el, false);
17721             var data = elements[id];
17722             if(data){
17723                 delete elements[id];
17724                 if(data.handles){
17725                     var hs = data.handles;
17726                     for(var i = 0, len = hs.length; i < len; i++){
17727                         delete handles[getId(hs[i], false)];
17728                     }
17729                 }
17730             }
17731         },
17732
17733     /**
17734      * Returns the handle registered for a DOM Node by id
17735      * @param {String|HTMLElement} id The DOM node or id to look up
17736      * @return {Object} handle The custom handle data
17737      */
17738         getHandle : function(id){
17739             if(typeof id != "string"){ // must be element?
17740                 id = id.id;
17741             }
17742             return handles[id];
17743         },
17744
17745     /**
17746      * Returns the handle that is registered for the DOM node that is the target of the event
17747      * @param {Event} e The event
17748      * @return {Object} handle The custom handle data
17749      */
17750         getHandleFromEvent : function(e){
17751             var t = Roo.lib.Event.getTarget(e);
17752             return t ? handles[t.id] : null;
17753         },
17754
17755     /**
17756      * Returns a custom data object that is registered for a DOM node by id
17757      * @param {String|HTMLElement} id The DOM node or id to look up
17758      * @return {Object} data The custom data
17759      */
17760         getTarget : function(id){
17761             if(typeof id != "string"){ // must be element?
17762                 id = id.id;
17763             }
17764             return elements[id];
17765         },
17766
17767     /**
17768      * Returns a custom data object that is registered for the DOM node that is the target of the event
17769      * @param {Event} e The event
17770      * @return {Object} data The custom data
17771      */
17772         getTargetFromEvent : function(e){
17773             var t = Roo.lib.Event.getTarget(e);
17774             return t ? elements[t.id] || handles[t.id] : null;
17775         }
17776     };
17777 }();/*
17778  * Based on:
17779  * Ext JS Library 1.1.1
17780  * Copyright(c) 2006-2007, Ext JS, LLC.
17781  *
17782  * Originally Released Under LGPL - original licence link has changed is not relivant.
17783  *
17784  * Fork - LGPL
17785  * <script type="text/javascript">
17786  */
17787  
17788
17789 /**
17790  * @class Roo.dd.StatusProxy
17791  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17792  * default drag proxy used by all Roo.dd components.
17793  * @constructor
17794  * @param {Object} config
17795  */
17796 Roo.dd.StatusProxy = function(config){
17797     Roo.apply(this, config);
17798     this.id = this.id || Roo.id();
17799     this.el = new Roo.Layer({
17800         dh: {
17801             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17802                 {tag: "div", cls: "x-dd-drop-icon"},
17803                 {tag: "div", cls: "x-dd-drag-ghost"}
17804             ]
17805         }, 
17806         shadow: !config || config.shadow !== false
17807     });
17808     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17809     this.dropStatus = this.dropNotAllowed;
17810 };
17811
17812 Roo.dd.StatusProxy.prototype = {
17813     /**
17814      * @cfg {String} dropAllowed
17815      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17816      */
17817     dropAllowed : "x-dd-drop-ok",
17818     /**
17819      * @cfg {String} dropNotAllowed
17820      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17821      */
17822     dropNotAllowed : "x-dd-drop-nodrop",
17823
17824     /**
17825      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17826      * over the current target element.
17827      * @param {String} cssClass The css class for the new drop status indicator image
17828      */
17829     setStatus : function(cssClass){
17830         cssClass = cssClass || this.dropNotAllowed;
17831         if(this.dropStatus != cssClass){
17832             this.el.replaceClass(this.dropStatus, cssClass);
17833             this.dropStatus = cssClass;
17834         }
17835     },
17836
17837     /**
17838      * Resets the status indicator to the default dropNotAllowed value
17839      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17840      */
17841     reset : function(clearGhost){
17842         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17843         this.dropStatus = this.dropNotAllowed;
17844         if(clearGhost){
17845             this.ghost.update("");
17846         }
17847     },
17848
17849     /**
17850      * Updates the contents of the ghost element
17851      * @param {String} html The html that will replace the current innerHTML of the ghost element
17852      */
17853     update : function(html){
17854         if(typeof html == "string"){
17855             this.ghost.update(html);
17856         }else{
17857             this.ghost.update("");
17858             html.style.margin = "0";
17859             this.ghost.dom.appendChild(html);
17860         }
17861         // ensure float = none set?? cant remember why though.
17862         var el = this.ghost.dom.firstChild;
17863                 if(el){
17864                         Roo.fly(el).setStyle('float', 'none');
17865                 }
17866     },
17867     
17868     /**
17869      * Returns the underlying proxy {@link Roo.Layer}
17870      * @return {Roo.Layer} el
17871     */
17872     getEl : function(){
17873         return this.el;
17874     },
17875
17876     /**
17877      * Returns the ghost element
17878      * @return {Roo.Element} el
17879      */
17880     getGhost : function(){
17881         return this.ghost;
17882     },
17883
17884     /**
17885      * Hides the proxy
17886      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17887      */
17888     hide : function(clear){
17889         this.el.hide();
17890         if(clear){
17891             this.reset(true);
17892         }
17893     },
17894
17895     /**
17896      * Stops the repair animation if it's currently running
17897      */
17898     stop : function(){
17899         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17900             this.anim.stop();
17901         }
17902     },
17903
17904     /**
17905      * Displays this proxy
17906      */
17907     show : function(){
17908         this.el.show();
17909     },
17910
17911     /**
17912      * Force the Layer to sync its shadow and shim positions to the element
17913      */
17914     sync : function(){
17915         this.el.sync();
17916     },
17917
17918     /**
17919      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17920      * invalid drop operation by the item being dragged.
17921      * @param {Array} xy The XY position of the element ([x, y])
17922      * @param {Function} callback The function to call after the repair is complete
17923      * @param {Object} scope The scope in which to execute the callback
17924      */
17925     repair : function(xy, callback, scope){
17926         this.callback = callback;
17927         this.scope = scope;
17928         if(xy && this.animRepair !== false){
17929             this.el.addClass("x-dd-drag-repair");
17930             this.el.hideUnders(true);
17931             this.anim = this.el.shift({
17932                 duration: this.repairDuration || .5,
17933                 easing: 'easeOut',
17934                 xy: xy,
17935                 stopFx: true,
17936                 callback: this.afterRepair,
17937                 scope: this
17938             });
17939         }else{
17940             this.afterRepair();
17941         }
17942     },
17943
17944     // private
17945     afterRepair : function(){
17946         this.hide(true);
17947         if(typeof this.callback == "function"){
17948             this.callback.call(this.scope || this);
17949         }
17950         this.callback = null;
17951         this.scope = null;
17952     }
17953 };/*
17954  * Based on:
17955  * Ext JS Library 1.1.1
17956  * Copyright(c) 2006-2007, Ext JS, LLC.
17957  *
17958  * Originally Released Under LGPL - original licence link has changed is not relivant.
17959  *
17960  * Fork - LGPL
17961  * <script type="text/javascript">
17962  */
17963
17964 /**
17965  * @class Roo.dd.DragSource
17966  * @extends Roo.dd.DDProxy
17967  * A simple class that provides the basic implementation needed to make any element draggable.
17968  * @constructor
17969  * @param {String/HTMLElement/Element} el The container element
17970  * @param {Object} config
17971  */
17972 Roo.dd.DragSource = function(el, config){
17973     this.el = Roo.get(el);
17974     this.dragData = {};
17975     
17976     Roo.apply(this, config);
17977     
17978     if(!this.proxy){
17979         this.proxy = new Roo.dd.StatusProxy();
17980     }
17981
17982     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17983           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17984     
17985     this.dragging = false;
17986 };
17987
17988 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17989     /**
17990      * @cfg {String} dropAllowed
17991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17992      */
17993     dropAllowed : "x-dd-drop-ok",
17994     /**
17995      * @cfg {String} dropNotAllowed
17996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17997      */
17998     dropNotAllowed : "x-dd-drop-nodrop",
17999
18000     /**
18001      * Returns the data object associated with this drag source
18002      * @return {Object} data An object containing arbitrary data
18003      */
18004     getDragData : function(e){
18005         return this.dragData;
18006     },
18007
18008     // private
18009     onDragEnter : function(e, id){
18010         var target = Roo.dd.DragDropMgr.getDDById(id);
18011         this.cachedTarget = target;
18012         if(this.beforeDragEnter(target, e, id) !== false){
18013             if(target.isNotifyTarget){
18014                 var status = target.notifyEnter(this, e, this.dragData);
18015                 this.proxy.setStatus(status);
18016             }else{
18017                 this.proxy.setStatus(this.dropAllowed);
18018             }
18019             
18020             if(this.afterDragEnter){
18021                 /**
18022                  * An empty function by default, but provided so that you can perform a custom action
18023                  * when the dragged item enters the drop target by providing an implementation.
18024                  * @param {Roo.dd.DragDrop} target The drop target
18025                  * @param {Event} e The event object
18026                  * @param {String} id The id of the dragged element
18027                  * @method afterDragEnter
18028                  */
18029                 this.afterDragEnter(target, e, id);
18030             }
18031         }
18032     },
18033
18034     /**
18035      * An empty function by default, but provided so that you can perform a custom action
18036      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18037      * @param {Roo.dd.DragDrop} target The drop target
18038      * @param {Event} e The event object
18039      * @param {String} id The id of the dragged element
18040      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18041      */
18042     beforeDragEnter : function(target, e, id){
18043         return true;
18044     },
18045
18046     // private
18047     alignElWithMouse: function() {
18048         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18049         this.proxy.sync();
18050     },
18051
18052     // private
18053     onDragOver : function(e, id){
18054         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18055         if(this.beforeDragOver(target, e, id) !== false){
18056             if(target.isNotifyTarget){
18057                 var status = target.notifyOver(this, e, this.dragData);
18058                 this.proxy.setStatus(status);
18059             }
18060
18061             if(this.afterDragOver){
18062                 /**
18063                  * An empty function by default, but provided so that you can perform a custom action
18064                  * while the dragged item is over the drop target by providing an implementation.
18065                  * @param {Roo.dd.DragDrop} target The drop target
18066                  * @param {Event} e The event object
18067                  * @param {String} id The id of the dragged element
18068                  * @method afterDragOver
18069                  */
18070                 this.afterDragOver(target, e, id);
18071             }
18072         }
18073     },
18074
18075     /**
18076      * An empty function by default, but provided so that you can perform a custom action
18077      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18078      * @param {Roo.dd.DragDrop} target The drop target
18079      * @param {Event} e The event object
18080      * @param {String} id The id of the dragged element
18081      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18082      */
18083     beforeDragOver : function(target, e, id){
18084         return true;
18085     },
18086
18087     // private
18088     onDragOut : function(e, id){
18089         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18090         if(this.beforeDragOut(target, e, id) !== false){
18091             if(target.isNotifyTarget){
18092                 target.notifyOut(this, e, this.dragData);
18093             }
18094             this.proxy.reset();
18095             if(this.afterDragOut){
18096                 /**
18097                  * An empty function by default, but provided so that you can perform a custom action
18098                  * after the dragged item is dragged out of the target without dropping.
18099                  * @param {Roo.dd.DragDrop} target The drop target
18100                  * @param {Event} e The event object
18101                  * @param {String} id The id of the dragged element
18102                  * @method afterDragOut
18103                  */
18104                 this.afterDragOut(target, e, id);
18105             }
18106         }
18107         this.cachedTarget = null;
18108     },
18109
18110     /**
18111      * An empty function by default, but provided so that you can perform a custom action before the dragged
18112      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18113      * @param {Roo.dd.DragDrop} target The drop target
18114      * @param {Event} e The event object
18115      * @param {String} id The id of the dragged element
18116      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18117      */
18118     beforeDragOut : function(target, e, id){
18119         return true;
18120     },
18121     
18122     // private
18123     onDragDrop : function(e, id){
18124         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18125         if(this.beforeDragDrop(target, e, id) !== false){
18126             if(target.isNotifyTarget){
18127                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18128                     this.onValidDrop(target, e, id);
18129                 }else{
18130                     this.onInvalidDrop(target, e, id);
18131                 }
18132             }else{
18133                 this.onValidDrop(target, e, id);
18134             }
18135             
18136             if(this.afterDragDrop){
18137                 /**
18138                  * An empty function by default, but provided so that you can perform a custom action
18139                  * after a valid drag drop has occurred by providing an implementation.
18140                  * @param {Roo.dd.DragDrop} target The drop target
18141                  * @param {Event} e The event object
18142                  * @param {String} id The id of the dropped element
18143                  * @method afterDragDrop
18144                  */
18145                 this.afterDragDrop(target, e, id);
18146             }
18147         }
18148         delete this.cachedTarget;
18149     },
18150
18151     /**
18152      * An empty function by default, but provided so that you can perform a custom action before the dragged
18153      * item is dropped onto the target and optionally cancel the onDragDrop.
18154      * @param {Roo.dd.DragDrop} target The drop target
18155      * @param {Event} e The event object
18156      * @param {String} id The id of the dragged element
18157      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18158      */
18159     beforeDragDrop : function(target, e, id){
18160         return true;
18161     },
18162
18163     // private
18164     onValidDrop : function(target, e, id){
18165         this.hideProxy();
18166         if(this.afterValidDrop){
18167             /**
18168              * An empty function by default, but provided so that you can perform a custom action
18169              * after a valid drop has occurred by providing an implementation.
18170              * @param {Object} target The target DD 
18171              * @param {Event} e The event object
18172              * @param {String} id The id of the dropped element
18173              * @method afterInvalidDrop
18174              */
18175             this.afterValidDrop(target, e, id);
18176         }
18177     },
18178
18179     // private
18180     getRepairXY : function(e, data){
18181         return this.el.getXY();  
18182     },
18183
18184     // private
18185     onInvalidDrop : function(target, e, id){
18186         this.beforeInvalidDrop(target, e, id);
18187         if(this.cachedTarget){
18188             if(this.cachedTarget.isNotifyTarget){
18189                 this.cachedTarget.notifyOut(this, e, this.dragData);
18190             }
18191             this.cacheTarget = null;
18192         }
18193         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18194
18195         if(this.afterInvalidDrop){
18196             /**
18197              * An empty function by default, but provided so that you can perform a custom action
18198              * after an invalid drop has occurred by providing an implementation.
18199              * @param {Event} e The event object
18200              * @param {String} id The id of the dropped element
18201              * @method afterInvalidDrop
18202              */
18203             this.afterInvalidDrop(e, id);
18204         }
18205     },
18206
18207     // private
18208     afterRepair : function(){
18209         if(Roo.enableFx){
18210             this.el.highlight(this.hlColor || "c3daf9");
18211         }
18212         this.dragging = false;
18213     },
18214
18215     /**
18216      * An empty function by default, but provided so that you can perform a custom action after an invalid
18217      * drop has occurred.
18218      * @param {Roo.dd.DragDrop} target The drop target
18219      * @param {Event} e The event object
18220      * @param {String} id The id of the dragged element
18221      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18222      */
18223     beforeInvalidDrop : function(target, e, id){
18224         return true;
18225     },
18226
18227     // private
18228     handleMouseDown : function(e){
18229         if(this.dragging) {
18230             return;
18231         }
18232         var data = this.getDragData(e);
18233         if(data && this.onBeforeDrag(data, e) !== false){
18234             this.dragData = data;
18235             this.proxy.stop();
18236             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18237         } 
18238     },
18239
18240     /**
18241      * An empty function by default, but provided so that you can perform a custom action before the initial
18242      * drag event begins and optionally cancel it.
18243      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18244      * @param {Event} e The event object
18245      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18246      */
18247     onBeforeDrag : function(data, e){
18248         return true;
18249     },
18250
18251     /**
18252      * An empty function by default, but provided so that you can perform a custom action once the initial
18253      * drag event has begun.  The drag cannot be canceled from this function.
18254      * @param {Number} x The x position of the click on the dragged object
18255      * @param {Number} y The y position of the click on the dragged object
18256      */
18257     onStartDrag : Roo.emptyFn,
18258
18259     // private - YUI override
18260     startDrag : function(x, y){
18261         this.proxy.reset();
18262         this.dragging = true;
18263         this.proxy.update("");
18264         this.onInitDrag(x, y);
18265         this.proxy.show();
18266     },
18267
18268     // private
18269     onInitDrag : function(x, y){
18270         var clone = this.el.dom.cloneNode(true);
18271         clone.id = Roo.id(); // prevent duplicate ids
18272         this.proxy.update(clone);
18273         this.onStartDrag(x, y);
18274         return true;
18275     },
18276
18277     /**
18278      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18279      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18280      */
18281     getProxy : function(){
18282         return this.proxy;  
18283     },
18284
18285     /**
18286      * Hides the drag source's {@link Roo.dd.StatusProxy}
18287      */
18288     hideProxy : function(){
18289         this.proxy.hide();  
18290         this.proxy.reset(true);
18291         this.dragging = false;
18292     },
18293
18294     // private
18295     triggerCacheRefresh : function(){
18296         Roo.dd.DDM.refreshCache(this.groups);
18297     },
18298
18299     // private - override to prevent hiding
18300     b4EndDrag: function(e) {
18301     },
18302
18303     // private - override to prevent moving
18304     endDrag : function(e){
18305         this.onEndDrag(this.dragData, e);
18306     },
18307
18308     // private
18309     onEndDrag : function(data, e){
18310     },
18311     
18312     // private - pin to cursor
18313     autoOffset : function(x, y) {
18314         this.setDelta(-12, -20);
18315     }    
18316 });/*
18317  * Based on:
18318  * Ext JS Library 1.1.1
18319  * Copyright(c) 2006-2007, Ext JS, LLC.
18320  *
18321  * Originally Released Under LGPL - original licence link has changed is not relivant.
18322  *
18323  * Fork - LGPL
18324  * <script type="text/javascript">
18325  */
18326
18327
18328 /**
18329  * @class Roo.dd.DropTarget
18330  * @extends Roo.dd.DDTarget
18331  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18332  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18333  * @constructor
18334  * @param {String/HTMLElement/Element} el The container element
18335  * @param {Object} config
18336  */
18337 Roo.dd.DropTarget = function(el, config){
18338     this.el = Roo.get(el);
18339     
18340     var listeners = false; ;
18341     if (config && config.listeners) {
18342         listeners= config.listeners;
18343         delete config.listeners;
18344     }
18345     Roo.apply(this, config);
18346     
18347     if(this.containerScroll){
18348         Roo.dd.ScrollManager.register(this.el);
18349     }
18350     this.addEvents( {
18351          /**
18352          * @scope Roo.dd.DropTarget
18353          */
18354          
18355          /**
18356          * @event enter
18357          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18358          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18359          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18360          * 
18361          * IMPORTANT : it should set this.overClass and this.dropAllowed
18362          * 
18363          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18364          * @param {Event} e The event
18365          * @param {Object} data An object containing arbitrary data supplied by the drag source
18366          */
18367         "enter" : true,
18368         
18369          /**
18370          * @event over
18371          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18372          * This method will be called on every mouse movement while the drag source is over the drop target.
18373          * This default implementation simply returns the dropAllowed config value.
18374          * 
18375          * IMPORTANT : it should set this.dropAllowed
18376          * 
18377          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18378          * @param {Event} e The event
18379          * @param {Object} data An object containing arbitrary data supplied by the drag source
18380          
18381          */
18382         "over" : true,
18383         /**
18384          * @event out
18385          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18386          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18387          * overClass (if any) from the drop element.
18388          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18389          * @param {Event} e The event
18390          * @param {Object} data An object containing arbitrary data supplied by the drag source
18391          */
18392          "out" : true,
18393          
18394         /**
18395          * @event drop
18396          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18397          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18398          * implementation that does something to process the drop event and returns true so that the drag source's
18399          * repair action does not run.
18400          * 
18401          * IMPORTANT : it should set this.success
18402          * 
18403          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18404          * @param {Event} e The event
18405          * @param {Object} data An object containing arbitrary data supplied by the drag source
18406         */
18407          "drop" : true
18408     });
18409             
18410      
18411     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18412         this.el.dom, 
18413         this.ddGroup || this.group,
18414         {
18415             isTarget: true,
18416             listeners : listeners || {} 
18417            
18418         
18419         }
18420     );
18421
18422 };
18423
18424 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18425     /**
18426      * @cfg {String} overClass
18427      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18428      */
18429      /**
18430      * @cfg {String} ddGroup
18431      * The drag drop group to handle drop events for
18432      */
18433      
18434     /**
18435      * @cfg {String} dropAllowed
18436      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18437      */
18438     dropAllowed : "x-dd-drop-ok",
18439     /**
18440      * @cfg {String} dropNotAllowed
18441      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18442      */
18443     dropNotAllowed : "x-dd-drop-nodrop",
18444     /**
18445      * @cfg {boolean} success
18446      * set this after drop listener.. 
18447      */
18448     success : false,
18449     /**
18450      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18451      * if the drop point is valid for over/enter..
18452      */
18453     valid : false,
18454     // private
18455     isTarget : true,
18456
18457     // private
18458     isNotifyTarget : true,
18459     
18460     /**
18461      * @hide
18462      */
18463     notifyEnter : function(dd, e, data)
18464     {
18465         this.valid = true;
18466         this.fireEvent('enter', dd, e, data);
18467         if(this.overClass){
18468             this.el.addClass(this.overClass);
18469         }
18470         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18471             this.valid ? this.dropAllowed : this.dropNotAllowed
18472         );
18473     },
18474
18475     /**
18476      * @hide
18477      */
18478     notifyOver : function(dd, e, data)
18479     {
18480         this.valid = true;
18481         this.fireEvent('over', dd, e, data);
18482         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18483             this.valid ? this.dropAllowed : this.dropNotAllowed
18484         );
18485     },
18486
18487     /**
18488      * @hide
18489      */
18490     notifyOut : function(dd, e, data)
18491     {
18492         this.fireEvent('out', dd, e, data);
18493         if(this.overClass){
18494             this.el.removeClass(this.overClass);
18495         }
18496     },
18497
18498     /**
18499      * @hide
18500      */
18501     notifyDrop : function(dd, e, data)
18502     {
18503         this.success = false;
18504         this.fireEvent('drop', dd, e, data);
18505         return this.success;
18506     }
18507 });/*
18508  * Based on:
18509  * Ext JS Library 1.1.1
18510  * Copyright(c) 2006-2007, Ext JS, LLC.
18511  *
18512  * Originally Released Under LGPL - original licence link has changed is not relivant.
18513  *
18514  * Fork - LGPL
18515  * <script type="text/javascript">
18516  */
18517
18518
18519 /**
18520  * @class Roo.dd.DragZone
18521  * @extends Roo.dd.DragSource
18522  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18523  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18524  * @constructor
18525  * @param {String/HTMLElement/Element} el The container element
18526  * @param {Object} config
18527  */
18528 Roo.dd.DragZone = function(el, config){
18529     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18530     if(this.containerScroll){
18531         Roo.dd.ScrollManager.register(this.el);
18532     }
18533 };
18534
18535 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18536     /**
18537      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18538      * for auto scrolling during drag operations.
18539      */
18540     /**
18541      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18542      * method after a failed drop (defaults to "c3daf9" - light blue)
18543      */
18544
18545     /**
18546      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18547      * for a valid target to drag based on the mouse down. Override this method
18548      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18549      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18550      * @param {EventObject} e The mouse down event
18551      * @return {Object} The dragData
18552      */
18553     getDragData : function(e){
18554         return Roo.dd.Registry.getHandleFromEvent(e);
18555     },
18556     
18557     /**
18558      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18559      * this.dragData.ddel
18560      * @param {Number} x The x position of the click on the dragged object
18561      * @param {Number} y The y position of the click on the dragged object
18562      * @return {Boolean} true to continue the drag, false to cancel
18563      */
18564     onInitDrag : function(x, y){
18565         this.proxy.update(this.dragData.ddel.cloneNode(true));
18566         this.onStartDrag(x, y);
18567         return true;
18568     },
18569     
18570     /**
18571      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18572      */
18573     afterRepair : function(){
18574         if(Roo.enableFx){
18575             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18576         }
18577         this.dragging = false;
18578     },
18579
18580     /**
18581      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18582      * the XY of this.dragData.ddel
18583      * @param {EventObject} e The mouse up event
18584      * @return {Array} The xy location (e.g. [100, 200])
18585      */
18586     getRepairXY : function(e){
18587         return Roo.Element.fly(this.dragData.ddel).getXY();  
18588     }
18589 });/*
18590  * Based on:
18591  * Ext JS Library 1.1.1
18592  * Copyright(c) 2006-2007, Ext JS, LLC.
18593  *
18594  * Originally Released Under LGPL - original licence link has changed is not relivant.
18595  *
18596  * Fork - LGPL
18597  * <script type="text/javascript">
18598  */
18599 /**
18600  * @class Roo.dd.DropZone
18601  * @extends Roo.dd.DropTarget
18602  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18603  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18604  * @constructor
18605  * @param {String/HTMLElement/Element} el The container element
18606  * @param {Object} config
18607  */
18608 Roo.dd.DropZone = function(el, config){
18609     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18610 };
18611
18612 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18613     /**
18614      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18615      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18616      * provide your own custom lookup.
18617      * @param {Event} e The event
18618      * @return {Object} data The custom data
18619      */
18620     getTargetFromEvent : function(e){
18621         return Roo.dd.Registry.getTargetFromEvent(e);
18622     },
18623
18624     /**
18625      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18626      * that it has registered.  This method has no default implementation and should be overridden to provide
18627      * node-specific processing if necessary.
18628      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18629      * {@link #getTargetFromEvent} for this node)
18630      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18631      * @param {Event} e The event
18632      * @param {Object} data An object containing arbitrary data supplied by the drag source
18633      */
18634     onNodeEnter : function(n, dd, e, data){
18635         
18636     },
18637
18638     /**
18639      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18640      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18641      * overridden to provide the proper feedback.
18642      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18643      * {@link #getTargetFromEvent} for this node)
18644      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18645      * @param {Event} e The event
18646      * @param {Object} data An object containing arbitrary data supplied by the drag source
18647      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18648      * underlying {@link Roo.dd.StatusProxy} can be updated
18649      */
18650     onNodeOver : function(n, dd, e, data){
18651         return this.dropAllowed;
18652     },
18653
18654     /**
18655      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18656      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18657      * node-specific processing if necessary.
18658      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18659      * {@link #getTargetFromEvent} for this node)
18660      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18661      * @param {Event} e The event
18662      * @param {Object} data An object containing arbitrary data supplied by the drag source
18663      */
18664     onNodeOut : function(n, dd, e, data){
18665         
18666     },
18667
18668     /**
18669      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18670      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18671      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18672      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18673      * {@link #getTargetFromEvent} for this node)
18674      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18675      * @param {Event} e The event
18676      * @param {Object} data An object containing arbitrary data supplied by the drag source
18677      * @return {Boolean} True if the drop was valid, else false
18678      */
18679     onNodeDrop : function(n, dd, e, data){
18680         return false;
18681     },
18682
18683     /**
18684      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18685      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18686      * it should be overridden to provide the proper feedback if necessary.
18687      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18688      * @param {Event} e The event
18689      * @param {Object} data An object containing arbitrary data supplied by the drag source
18690      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18691      * underlying {@link Roo.dd.StatusProxy} can be updated
18692      */
18693     onContainerOver : function(dd, e, data){
18694         return this.dropNotAllowed;
18695     },
18696
18697     /**
18698      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18699      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18700      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18701      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18702      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18703      * @param {Event} e The event
18704      * @param {Object} data An object containing arbitrary data supplied by the drag source
18705      * @return {Boolean} True if the drop was valid, else false
18706      */
18707     onContainerDrop : function(dd, e, data){
18708         return false;
18709     },
18710
18711     /**
18712      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18713      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18714      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18715      * you should override this method and provide a custom implementation.
18716      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18717      * @param {Event} e The event
18718      * @param {Object} data An object containing arbitrary data supplied by the drag source
18719      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18720      * underlying {@link Roo.dd.StatusProxy} can be updated
18721      */
18722     notifyEnter : function(dd, e, data){
18723         return this.dropNotAllowed;
18724     },
18725
18726     /**
18727      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18728      * This method will be called on every mouse movement while the drag source is over the drop zone.
18729      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18730      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18731      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18732      * registered node, it will call {@link #onContainerOver}.
18733      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18734      * @param {Event} e The event
18735      * @param {Object} data An object containing arbitrary data supplied by the drag source
18736      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18737      * underlying {@link Roo.dd.StatusProxy} can be updated
18738      */
18739     notifyOver : function(dd, e, data){
18740         var n = this.getTargetFromEvent(e);
18741         if(!n){ // not over valid drop target
18742             if(this.lastOverNode){
18743                 this.onNodeOut(this.lastOverNode, dd, e, data);
18744                 this.lastOverNode = null;
18745             }
18746             return this.onContainerOver(dd, e, data);
18747         }
18748         if(this.lastOverNode != n){
18749             if(this.lastOverNode){
18750                 this.onNodeOut(this.lastOverNode, dd, e, data);
18751             }
18752             this.onNodeEnter(n, dd, e, data);
18753             this.lastOverNode = n;
18754         }
18755         return this.onNodeOver(n, dd, e, data);
18756     },
18757
18758     /**
18759      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18760      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18761      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18762      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18763      * @param {Event} e The event
18764      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18765      */
18766     notifyOut : function(dd, e, data){
18767         if(this.lastOverNode){
18768             this.onNodeOut(this.lastOverNode, dd, e, data);
18769             this.lastOverNode = null;
18770         }
18771     },
18772
18773     /**
18774      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18775      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18776      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18777      * otherwise it will call {@link #onContainerDrop}.
18778      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18779      * @param {Event} e The event
18780      * @param {Object} data An object containing arbitrary data supplied by the drag source
18781      * @return {Boolean} True if the drop was valid, else false
18782      */
18783     notifyDrop : function(dd, e, data){
18784         if(this.lastOverNode){
18785             this.onNodeOut(this.lastOverNode, dd, e, data);
18786             this.lastOverNode = null;
18787         }
18788         var n = this.getTargetFromEvent(e);
18789         return n ?
18790             this.onNodeDrop(n, dd, e, data) :
18791             this.onContainerDrop(dd, e, data);
18792     },
18793
18794     // private
18795     triggerCacheRefresh : function(){
18796         Roo.dd.DDM.refreshCache(this.groups);
18797     }  
18798 });/*
18799  * Based on:
18800  * Ext JS Library 1.1.1
18801  * Copyright(c) 2006-2007, Ext JS, LLC.
18802  *
18803  * Originally Released Under LGPL - original licence link has changed is not relivant.
18804  *
18805  * Fork - LGPL
18806  * <script type="text/javascript">
18807  */
18808
18809
18810 /**
18811  * @class Roo.data.SortTypes
18812  * @singleton
18813  * Defines the default sorting (casting?) comparison functions used when sorting data.
18814  */
18815 Roo.data.SortTypes = {
18816     /**
18817      * Default sort that does nothing
18818      * @param {Mixed} s The value being converted
18819      * @return {Mixed} The comparison value
18820      */
18821     none : function(s){
18822         return s;
18823     },
18824     
18825     /**
18826      * The regular expression used to strip tags
18827      * @type {RegExp}
18828      * @property
18829      */
18830     stripTagsRE : /<\/?[^>]+>/gi,
18831     
18832     /**
18833      * Strips all HTML tags to sort on text only
18834      * @param {Mixed} s The value being converted
18835      * @return {String} The comparison value
18836      */
18837     asText : function(s){
18838         return String(s).replace(this.stripTagsRE, "");
18839     },
18840     
18841     /**
18842      * Strips all HTML tags to sort on text only - Case insensitive
18843      * @param {Mixed} s The value being converted
18844      * @return {String} The comparison value
18845      */
18846     asUCText : function(s){
18847         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18848     },
18849     
18850     /**
18851      * Case insensitive string
18852      * @param {Mixed} s The value being converted
18853      * @return {String} The comparison value
18854      */
18855     asUCString : function(s) {
18856         return String(s).toUpperCase();
18857     },
18858     
18859     /**
18860      * Date sorting
18861      * @param {Mixed} s The value being converted
18862      * @return {Number} The comparison value
18863      */
18864     asDate : function(s) {
18865         if(!s){
18866             return 0;
18867         }
18868         if(s instanceof Date){
18869             return s.getTime();
18870         }
18871         return Date.parse(String(s));
18872     },
18873     
18874     /**
18875      * Float sorting
18876      * @param {Mixed} s The value being converted
18877      * @return {Float} The comparison value
18878      */
18879     asFloat : function(s) {
18880         var val = parseFloat(String(s).replace(/,/g, ""));
18881         if(isNaN(val)) val = 0;
18882         return val;
18883     },
18884     
18885     /**
18886      * Integer sorting
18887      * @param {Mixed} s The value being converted
18888      * @return {Number} The comparison value
18889      */
18890     asInt : function(s) {
18891         var val = parseInt(String(s).replace(/,/g, ""));
18892         if(isNaN(val)) val = 0;
18893         return val;
18894     }
18895 };/*
18896  * Based on:
18897  * Ext JS Library 1.1.1
18898  * Copyright(c) 2006-2007, Ext JS, LLC.
18899  *
18900  * Originally Released Under LGPL - original licence link has changed is not relivant.
18901  *
18902  * Fork - LGPL
18903  * <script type="text/javascript">
18904  */
18905
18906 /**
18907 * @class Roo.data.Record
18908  * Instances of this class encapsulate both record <em>definition</em> information, and record
18909  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18910  * to access Records cached in an {@link Roo.data.Store} object.<br>
18911  * <p>
18912  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18913  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18914  * objects.<br>
18915  * <p>
18916  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18917  * @constructor
18918  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18919  * {@link #create}. The parameters are the same.
18920  * @param {Array} data An associative Array of data values keyed by the field name.
18921  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18922  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18923  * not specified an integer id is generated.
18924  */
18925 Roo.data.Record = function(data, id){
18926     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18927     this.data = data;
18928 };
18929
18930 /**
18931  * Generate a constructor for a specific record layout.
18932  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18933  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18934  * Each field definition object may contain the following properties: <ul>
18935  * <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,
18936  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18937  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18938  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18939  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18940  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18941  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18942  * this may be omitted.</p></li>
18943  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18944  * <ul><li>auto (Default, implies no conversion)</li>
18945  * <li>string</li>
18946  * <li>int</li>
18947  * <li>float</li>
18948  * <li>boolean</li>
18949  * <li>date</li></ul></p></li>
18950  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18951  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18952  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18953  * by the Reader into an object that will be stored in the Record. It is passed the
18954  * following parameters:<ul>
18955  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18956  * </ul></p></li>
18957  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18958  * </ul>
18959  * <br>usage:<br><pre><code>
18960 var TopicRecord = Roo.data.Record.create(
18961     {name: 'title', mapping: 'topic_title'},
18962     {name: 'author', mapping: 'username'},
18963     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18964     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18965     {name: 'lastPoster', mapping: 'user2'},
18966     {name: 'excerpt', mapping: 'post_text'}
18967 );
18968
18969 var myNewRecord = new TopicRecord({
18970     title: 'Do my job please',
18971     author: 'noobie',
18972     totalPosts: 1,
18973     lastPost: new Date(),
18974     lastPoster: 'Animal',
18975     excerpt: 'No way dude!'
18976 });
18977 myStore.add(myNewRecord);
18978 </code></pre>
18979  * @method create
18980  * @static
18981  */
18982 Roo.data.Record.create = function(o){
18983     var f = function(){
18984         f.superclass.constructor.apply(this, arguments);
18985     };
18986     Roo.extend(f, Roo.data.Record);
18987     var p = f.prototype;
18988     p.fields = new Roo.util.MixedCollection(false, function(field){
18989         return field.name;
18990     });
18991     for(var i = 0, len = o.length; i < len; i++){
18992         p.fields.add(new Roo.data.Field(o[i]));
18993     }
18994     f.getField = function(name){
18995         return p.fields.get(name);  
18996     };
18997     return f;
18998 };
18999
19000 Roo.data.Record.AUTO_ID = 1000;
19001 Roo.data.Record.EDIT = 'edit';
19002 Roo.data.Record.REJECT = 'reject';
19003 Roo.data.Record.COMMIT = 'commit';
19004
19005 Roo.data.Record.prototype = {
19006     /**
19007      * Readonly flag - true if this record has been modified.
19008      * @type Boolean
19009      */
19010     dirty : false,
19011     editing : false,
19012     error: null,
19013     modified: null,
19014
19015     // private
19016     join : function(store){
19017         this.store = store;
19018     },
19019
19020     /**
19021      * Set the named field to the specified value.
19022      * @param {String} name The name of the field to set.
19023      * @param {Object} value The value to set the field to.
19024      */
19025     set : function(name, value){
19026         if(this.data[name] == value){
19027             return;
19028         }
19029         this.dirty = true;
19030         if(!this.modified){
19031             this.modified = {};
19032         }
19033         if(typeof this.modified[name] == 'undefined'){
19034             this.modified[name] = this.data[name];
19035         }
19036         this.data[name] = value;
19037         if(!this.editing){
19038             this.store.afterEdit(this);
19039         }       
19040     },
19041
19042     /**
19043      * Get the value of the named field.
19044      * @param {String} name The name of the field to get the value of.
19045      * @return {Object} The value of the field.
19046      */
19047     get : function(name){
19048         return this.data[name]; 
19049     },
19050
19051     // private
19052     beginEdit : function(){
19053         this.editing = true;
19054         this.modified = {}; 
19055     },
19056
19057     // private
19058     cancelEdit : function(){
19059         this.editing = false;
19060         delete this.modified;
19061     },
19062
19063     // private
19064     endEdit : function(){
19065         this.editing = false;
19066         if(this.dirty && this.store){
19067             this.store.afterEdit(this);
19068         }
19069     },
19070
19071     /**
19072      * Usually called by the {@link Roo.data.Store} which owns the Record.
19073      * Rejects all changes made to the Record since either creation, or the last commit operation.
19074      * Modified fields are reverted to their original values.
19075      * <p>
19076      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19077      * of reject operations.
19078      */
19079     reject : function(){
19080         var m = this.modified;
19081         for(var n in m){
19082             if(typeof m[n] != "function"){
19083                 this.data[n] = m[n];
19084             }
19085         }
19086         this.dirty = false;
19087         delete this.modified;
19088         this.editing = false;
19089         if(this.store){
19090             this.store.afterReject(this);
19091         }
19092     },
19093
19094     /**
19095      * Usually called by the {@link Roo.data.Store} which owns the Record.
19096      * Commits all changes made to the Record since either creation, or the last commit operation.
19097      * <p>
19098      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19099      * of commit operations.
19100      */
19101     commit : function(){
19102         this.dirty = false;
19103         delete this.modified;
19104         this.editing = false;
19105         if(this.store){
19106             this.store.afterCommit(this);
19107         }
19108     },
19109
19110     // private
19111     hasError : function(){
19112         return this.error != null;
19113     },
19114
19115     // private
19116     clearError : function(){
19117         this.error = null;
19118     },
19119
19120     /**
19121      * Creates a copy of this record.
19122      * @param {String} id (optional) A new record id if you don't want to use this record's id
19123      * @return {Record}
19124      */
19125     copy : function(newId) {
19126         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19127     }
19128 };/*
19129  * Based on:
19130  * Ext JS Library 1.1.1
19131  * Copyright(c) 2006-2007, Ext JS, LLC.
19132  *
19133  * Originally Released Under LGPL - original licence link has changed is not relivant.
19134  *
19135  * Fork - LGPL
19136  * <script type="text/javascript">
19137  */
19138
19139
19140
19141 /**
19142  * @class Roo.data.Store
19143  * @extends Roo.util.Observable
19144  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19145  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19146  * <p>
19147  * 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
19148  * has no knowledge of the format of the data returned by the Proxy.<br>
19149  * <p>
19150  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19151  * instances from the data object. These records are cached and made available through accessor functions.
19152  * @constructor
19153  * Creates a new Store.
19154  * @param {Object} config A config object containing the objects needed for the Store to access data,
19155  * and read the data into Records.
19156  */
19157 Roo.data.Store = function(config){
19158     this.data = new Roo.util.MixedCollection(false);
19159     this.data.getKey = function(o){
19160         return o.id;
19161     };
19162     this.baseParams = {};
19163     // private
19164     this.paramNames = {
19165         "start" : "start",
19166         "limit" : "limit",
19167         "sort" : "sort",
19168         "dir" : "dir"
19169     };
19170
19171     if(config && config.data){
19172         this.inlineData = config.data;
19173         delete config.data;
19174     }
19175
19176     Roo.apply(this, config);
19177     
19178     if(this.reader){ // reader passed
19179         this.reader = Roo.factory(this.reader, Roo.data);
19180         this.reader.xmodule = this.xmodule || false;
19181         if(!this.recordType){
19182             this.recordType = this.reader.recordType;
19183         }
19184         if(this.reader.onMetaChange){
19185             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19186         }
19187     }
19188
19189     if(this.recordType){
19190         this.fields = this.recordType.prototype.fields;
19191     }
19192     this.modified = [];
19193
19194     this.addEvents({
19195         /**
19196          * @event datachanged
19197          * Fires when the data cache has changed, and a widget which is using this Store
19198          * as a Record cache should refresh its view.
19199          * @param {Store} this
19200          */
19201         datachanged : true,
19202         /**
19203          * @event metachange
19204          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19205          * @param {Store} this
19206          * @param {Object} meta The JSON metadata
19207          */
19208         metachange : true,
19209         /**
19210          * @event add
19211          * Fires when Records have been added to the Store
19212          * @param {Store} this
19213          * @param {Roo.data.Record[]} records The array of Records added
19214          * @param {Number} index The index at which the record(s) were added
19215          */
19216         add : true,
19217         /**
19218          * @event remove
19219          * Fires when a Record has been removed from the Store
19220          * @param {Store} this
19221          * @param {Roo.data.Record} record The Record that was removed
19222          * @param {Number} index The index at which the record was removed
19223          */
19224         remove : true,
19225         /**
19226          * @event update
19227          * Fires when a Record has been updated
19228          * @param {Store} this
19229          * @param {Roo.data.Record} record The Record that was updated
19230          * @param {String} operation The update operation being performed.  Value may be one of:
19231          * <pre><code>
19232  Roo.data.Record.EDIT
19233  Roo.data.Record.REJECT
19234  Roo.data.Record.COMMIT
19235          * </code></pre>
19236          */
19237         update : true,
19238         /**
19239          * @event clear
19240          * Fires when the data cache has been cleared.
19241          * @param {Store} this
19242          */
19243         clear : true,
19244         /**
19245          * @event beforeload
19246          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19247          * the load action will be canceled.
19248          * @param {Store} this
19249          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19250          */
19251         beforeload : true,
19252         /**
19253          * @event load
19254          * Fires after a new set of Records has been loaded.
19255          * @param {Store} this
19256          * @param {Roo.data.Record[]} records The Records that were loaded
19257          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19258          */
19259         load : true,
19260         /**
19261          * @event loadexception
19262          * Fires if an exception occurs in the Proxy during loading.
19263          * Called with the signature of the Proxy's "loadexception" event.
19264          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19265          * 
19266          * @param {Proxy} 
19267          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19268          * @param {Object} load options 
19269          * @param {Object} jsonData from your request (normally this contains the Exception)
19270          */
19271         loadexception : true
19272     });
19273     
19274     if(this.proxy){
19275         this.proxy = Roo.factory(this.proxy, Roo.data);
19276         this.proxy.xmodule = this.xmodule || false;
19277         this.relayEvents(this.proxy,  ["loadexception"]);
19278     }
19279     this.sortToggle = {};
19280
19281     Roo.data.Store.superclass.constructor.call(this);
19282
19283     if(this.inlineData){
19284         this.loadData(this.inlineData);
19285         delete this.inlineData;
19286     }
19287 };
19288 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19289      /**
19290     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19291     * without a remote query - used by combo/forms at present.
19292     */
19293     
19294     /**
19295     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19296     */
19297     /**
19298     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19299     */
19300     /**
19301     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19302     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19303     */
19304     /**
19305     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19306     * on any HTTP request
19307     */
19308     /**
19309     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19310     */
19311     /**
19312     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19313     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19314     */
19315     remoteSort : false,
19316
19317     /**
19318     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19319      * loaded or when a record is removed. (defaults to false).
19320     */
19321     pruneModifiedRecords : false,
19322
19323     // private
19324     lastOptions : null,
19325
19326     /**
19327      * Add Records to the Store and fires the add event.
19328      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19329      */
19330     add : function(records){
19331         records = [].concat(records);
19332         for(var i = 0, len = records.length; i < len; i++){
19333             records[i].join(this);
19334         }
19335         var index = this.data.length;
19336         this.data.addAll(records);
19337         this.fireEvent("add", this, records, index);
19338     },
19339
19340     /**
19341      * Remove a Record from the Store and fires the remove event.
19342      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19343      */
19344     remove : function(record){
19345         var index = this.data.indexOf(record);
19346         this.data.removeAt(index);
19347         if(this.pruneModifiedRecords){
19348             this.modified.remove(record);
19349         }
19350         this.fireEvent("remove", this, record, index);
19351     },
19352
19353     /**
19354      * Remove all Records from the Store and fires the clear event.
19355      */
19356     removeAll : function(){
19357         this.data.clear();
19358         if(this.pruneModifiedRecords){
19359             this.modified = [];
19360         }
19361         this.fireEvent("clear", this);
19362     },
19363
19364     /**
19365      * Inserts Records to the Store at the given index and fires the add event.
19366      * @param {Number} index The start index at which to insert the passed Records.
19367      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19368      */
19369     insert : function(index, records){
19370         records = [].concat(records);
19371         for(var i = 0, len = records.length; i < len; i++){
19372             this.data.insert(index, records[i]);
19373             records[i].join(this);
19374         }
19375         this.fireEvent("add", this, records, index);
19376     },
19377
19378     /**
19379      * Get the index within the cache of the passed Record.
19380      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19381      * @return {Number} The index of the passed Record. Returns -1 if not found.
19382      */
19383     indexOf : function(record){
19384         return this.data.indexOf(record);
19385     },
19386
19387     /**
19388      * Get the index within the cache of the Record with the passed id.
19389      * @param {String} id The id of the Record to find.
19390      * @return {Number} The index of the Record. Returns -1 if not found.
19391      */
19392     indexOfId : function(id){
19393         return this.data.indexOfKey(id);
19394     },
19395
19396     /**
19397      * Get the Record with the specified id.
19398      * @param {String} id The id of the Record to find.
19399      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19400      */
19401     getById : function(id){
19402         return this.data.key(id);
19403     },
19404
19405     /**
19406      * Get the Record at the specified index.
19407      * @param {Number} index The index of the Record to find.
19408      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19409      */
19410     getAt : function(index){
19411         return this.data.itemAt(index);
19412     },
19413
19414     /**
19415      * Returns a range of Records between specified indices.
19416      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19417      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19418      * @return {Roo.data.Record[]} An array of Records
19419      */
19420     getRange : function(start, end){
19421         return this.data.getRange(start, end);
19422     },
19423
19424     // private
19425     storeOptions : function(o){
19426         o = Roo.apply({}, o);
19427         delete o.callback;
19428         delete o.scope;
19429         this.lastOptions = o;
19430     },
19431
19432     /**
19433      * Loads the Record cache from the configured Proxy using the configured Reader.
19434      * <p>
19435      * If using remote paging, then the first load call must specify the <em>start</em>
19436      * and <em>limit</em> properties in the options.params property to establish the initial
19437      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19438      * <p>
19439      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19440      * and this call will return before the new data has been loaded. Perform any post-processing
19441      * in a callback function, or in a "load" event handler.</strong>
19442      * <p>
19443      * @param {Object} options An object containing properties which control loading options:<ul>
19444      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19445      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19446      * passed the following arguments:<ul>
19447      * <li>r : Roo.data.Record[]</li>
19448      * <li>options: Options object from the load call</li>
19449      * <li>success: Boolean success indicator</li></ul></li>
19450      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19451      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19452      * </ul>
19453      */
19454     load : function(options){
19455         options = options || {};
19456         if(this.fireEvent("beforeload", this, options) !== false){
19457             this.storeOptions(options);
19458             var p = Roo.apply(options.params || {}, this.baseParams);
19459             // if meta was not loaded from remote source.. try requesting it.
19460             if (!this.reader.metaFromRemote) {
19461                 p._requestMeta = 1;
19462             }
19463             if(this.sortInfo && this.remoteSort){
19464                 var pn = this.paramNames;
19465                 p[pn["sort"]] = this.sortInfo.field;
19466                 p[pn["dir"]] = this.sortInfo.direction;
19467             }
19468             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19469         }
19470     },
19471
19472     /**
19473      * Reloads the Record cache from the configured Proxy using the configured Reader and
19474      * the options from the last load operation performed.
19475      * @param {Object} options (optional) An object containing properties which may override the options
19476      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19477      * the most recently used options are reused).
19478      */
19479     reload : function(options){
19480         this.load(Roo.applyIf(options||{}, this.lastOptions));
19481     },
19482
19483     // private
19484     // Called as a callback by the Reader during a load operation.
19485     loadRecords : function(o, options, success){
19486         if(!o || success === false){
19487             if(success !== false){
19488                 this.fireEvent("load", this, [], options);
19489             }
19490             if(options.callback){
19491                 options.callback.call(options.scope || this, [], options, false);
19492             }
19493             return;
19494         }
19495         // if data returned failure - throw an exception.
19496         if (o.success === false) {
19497             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19498             return;
19499         }
19500         var r = o.records, t = o.totalRecords || r.length;
19501         if(!options || options.add !== true){
19502             if(this.pruneModifiedRecords){
19503                 this.modified = [];
19504             }
19505             for(var i = 0, len = r.length; i < len; i++){
19506                 r[i].join(this);
19507             }
19508             if(this.snapshot){
19509                 this.data = this.snapshot;
19510                 delete this.snapshot;
19511             }
19512             this.data.clear();
19513             this.data.addAll(r);
19514             this.totalLength = t;
19515             this.applySort();
19516             this.fireEvent("datachanged", this);
19517         }else{
19518             this.totalLength = Math.max(t, this.data.length+r.length);
19519             this.add(r);
19520         }
19521         this.fireEvent("load", this, r, options);
19522         if(options.callback){
19523             options.callback.call(options.scope || this, r, options, true);
19524         }
19525     },
19526
19527     /**
19528      * Loads data from a passed data block. A Reader which understands the format of the data
19529      * must have been configured in the constructor.
19530      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19531      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19532      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19533      */
19534     loadData : function(o, append){
19535         var r = this.reader.readRecords(o);
19536         this.loadRecords(r, {add: append}, true);
19537     },
19538
19539     /**
19540      * Gets the number of cached records.
19541      * <p>
19542      * <em>If using paging, this may not be the total size of the dataset. If the data object
19543      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19544      * the data set size</em>
19545      */
19546     getCount : function(){
19547         return this.data.length || 0;
19548     },
19549
19550     /**
19551      * Gets the total number of records in the dataset as returned by the server.
19552      * <p>
19553      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19554      * the dataset size</em>
19555      */
19556     getTotalCount : function(){
19557         return this.totalLength || 0;
19558     },
19559
19560     /**
19561      * Returns the sort state of the Store as an object with two properties:
19562      * <pre><code>
19563  field {String} The name of the field by which the Records are sorted
19564  direction {String} The sort order, "ASC" or "DESC"
19565      * </code></pre>
19566      */
19567     getSortState : function(){
19568         return this.sortInfo;
19569     },
19570
19571     // private
19572     applySort : function(){
19573         if(this.sortInfo && !this.remoteSort){
19574             var s = this.sortInfo, f = s.field;
19575             var st = this.fields.get(f).sortType;
19576             var fn = function(r1, r2){
19577                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19578                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19579             };
19580             this.data.sort(s.direction, fn);
19581             if(this.snapshot && this.snapshot != this.data){
19582                 this.snapshot.sort(s.direction, fn);
19583             }
19584         }
19585     },
19586
19587     /**
19588      * Sets the default sort column and order to be used by the next load operation.
19589      * @param {String} fieldName The name of the field to sort by.
19590      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19591      */
19592     setDefaultSort : function(field, dir){
19593         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19594     },
19595
19596     /**
19597      * Sort the Records.
19598      * If remote sorting is used, the sort is performed on the server, and the cache is
19599      * reloaded. If local sorting is used, the cache is sorted internally.
19600      * @param {String} fieldName The name of the field to sort by.
19601      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19602      */
19603     sort : function(fieldName, dir){
19604         var f = this.fields.get(fieldName);
19605         if(!dir){
19606             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19607                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19608             }else{
19609                 dir = f.sortDir;
19610             }
19611         }
19612         this.sortToggle[f.name] = dir;
19613         this.sortInfo = {field: f.name, direction: dir};
19614         if(!this.remoteSort){
19615             this.applySort();
19616             this.fireEvent("datachanged", this);
19617         }else{
19618             this.load(this.lastOptions);
19619         }
19620     },
19621
19622     /**
19623      * Calls the specified function for each of the Records in the cache.
19624      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19625      * Returning <em>false</em> aborts and exits the iteration.
19626      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19627      */
19628     each : function(fn, scope){
19629         this.data.each(fn, scope);
19630     },
19631
19632     /**
19633      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19634      * (e.g., during paging).
19635      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19636      */
19637     getModifiedRecords : function(){
19638         return this.modified;
19639     },
19640
19641     // private
19642     createFilterFn : function(property, value, anyMatch){
19643         if(!value.exec){ // not a regex
19644             value = String(value);
19645             if(value.length == 0){
19646                 return false;
19647             }
19648             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19649         }
19650         return function(r){
19651             return value.test(r.data[property]);
19652         };
19653     },
19654
19655     /**
19656      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19657      * @param {String} property A field on your records
19658      * @param {Number} start The record index to start at (defaults to 0)
19659      * @param {Number} end The last record index to include (defaults to length - 1)
19660      * @return {Number} The sum
19661      */
19662     sum : function(property, start, end){
19663         var rs = this.data.items, v = 0;
19664         start = start || 0;
19665         end = (end || end === 0) ? end : rs.length-1;
19666
19667         for(var i = start; i <= end; i++){
19668             v += (rs[i].data[property] || 0);
19669         }
19670         return v;
19671     },
19672
19673     /**
19674      * Filter the records by a specified property.
19675      * @param {String} field A field on your records
19676      * @param {String/RegExp} value Either a string that the field
19677      * should start with or a RegExp to test against the field
19678      * @param {Boolean} anyMatch True to match any part not just the beginning
19679      */
19680     filter : function(property, value, anyMatch){
19681         var fn = this.createFilterFn(property, value, anyMatch);
19682         return fn ? this.filterBy(fn) : this.clearFilter();
19683     },
19684
19685     /**
19686      * Filter by a function. The specified function will be called with each
19687      * record in this data source. If the function returns true the record is included,
19688      * otherwise it is filtered.
19689      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19690      * @param {Object} scope (optional) The scope of the function (defaults to this)
19691      */
19692     filterBy : function(fn, scope){
19693         this.snapshot = this.snapshot || this.data;
19694         this.data = this.queryBy(fn, scope||this);
19695         this.fireEvent("datachanged", this);
19696     },
19697
19698     /**
19699      * Query the records by a specified property.
19700      * @param {String} field A field on your records
19701      * @param {String/RegExp} value Either a string that the field
19702      * should start with or a RegExp to test against the field
19703      * @param {Boolean} anyMatch True to match any part not just the beginning
19704      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19705      */
19706     query : function(property, value, anyMatch){
19707         var fn = this.createFilterFn(property, value, anyMatch);
19708         return fn ? this.queryBy(fn) : this.data.clone();
19709     },
19710
19711     /**
19712      * Query by a function. The specified function will be called with each
19713      * record in this data source. If the function returns true the record is included
19714      * in the results.
19715      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19716      * @param {Object} scope (optional) The scope of the function (defaults to this)
19717       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19718      **/
19719     queryBy : function(fn, scope){
19720         var data = this.snapshot || this.data;
19721         return data.filterBy(fn, scope||this);
19722     },
19723
19724     /**
19725      * Collects unique values for a particular dataIndex from this store.
19726      * @param {String} dataIndex The property to collect
19727      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19728      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19729      * @return {Array} An array of the unique values
19730      **/
19731     collect : function(dataIndex, allowNull, bypassFilter){
19732         var d = (bypassFilter === true && this.snapshot) ?
19733                 this.snapshot.items : this.data.items;
19734         var v, sv, r = [], l = {};
19735         for(var i = 0, len = d.length; i < len; i++){
19736             v = d[i].data[dataIndex];
19737             sv = String(v);
19738             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19739                 l[sv] = true;
19740                 r[r.length] = v;
19741             }
19742         }
19743         return r;
19744     },
19745
19746     /**
19747      * Revert to a view of the Record cache with no filtering applied.
19748      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19749      */
19750     clearFilter : function(suppressEvent){
19751         if(this.snapshot && this.snapshot != this.data){
19752             this.data = this.snapshot;
19753             delete this.snapshot;
19754             if(suppressEvent !== true){
19755                 this.fireEvent("datachanged", this);
19756             }
19757         }
19758     },
19759
19760     // private
19761     afterEdit : function(record){
19762         if(this.modified.indexOf(record) == -1){
19763             this.modified.push(record);
19764         }
19765         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19766     },
19767
19768     // private
19769     afterReject : function(record){
19770         this.modified.remove(record);
19771         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19772     },
19773
19774     // private
19775     afterCommit : function(record){
19776         this.modified.remove(record);
19777         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19778     },
19779
19780     /**
19781      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19782      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19783      */
19784     commitChanges : function(){
19785         var m = this.modified.slice(0);
19786         this.modified = [];
19787         for(var i = 0, len = m.length; i < len; i++){
19788             m[i].commit();
19789         }
19790     },
19791
19792     /**
19793      * Cancel outstanding changes on all changed records.
19794      */
19795     rejectChanges : function(){
19796         var m = this.modified.slice(0);
19797         this.modified = [];
19798         for(var i = 0, len = m.length; i < len; i++){
19799             m[i].reject();
19800         }
19801     },
19802
19803     onMetaChange : function(meta, rtype, o){
19804         this.recordType = rtype;
19805         this.fields = rtype.prototype.fields;
19806         delete this.snapshot;
19807         this.sortInfo = meta.sortInfo || this.sortInfo;
19808         this.modified = [];
19809         this.fireEvent('metachange', this, this.reader.meta);
19810     }
19811 });/*
19812  * Based on:
19813  * Ext JS Library 1.1.1
19814  * Copyright(c) 2006-2007, Ext JS, LLC.
19815  *
19816  * Originally Released Under LGPL - original licence link has changed is not relivant.
19817  *
19818  * Fork - LGPL
19819  * <script type="text/javascript">
19820  */
19821
19822 /**
19823  * @class Roo.data.SimpleStore
19824  * @extends Roo.data.Store
19825  * Small helper class to make creating Stores from Array data easier.
19826  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19827  * @cfg {Array} fields An array of field definition objects, or field name strings.
19828  * @cfg {Array} data The multi-dimensional array of data
19829  * @constructor
19830  * @param {Object} config
19831  */
19832 Roo.data.SimpleStore = function(config){
19833     Roo.data.SimpleStore.superclass.constructor.call(this, {
19834         isLocal : true,
19835         reader: new Roo.data.ArrayReader({
19836                 id: config.id
19837             },
19838             Roo.data.Record.create(config.fields)
19839         ),
19840         proxy : new Roo.data.MemoryProxy(config.data)
19841     });
19842     this.load();
19843 };
19844 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19845  * Based on:
19846  * Ext JS Library 1.1.1
19847  * Copyright(c) 2006-2007, Ext JS, LLC.
19848  *
19849  * Originally Released Under LGPL - original licence link has changed is not relivant.
19850  *
19851  * Fork - LGPL
19852  * <script type="text/javascript">
19853  */
19854
19855 /**
19856 /**
19857  * @extends Roo.data.Store
19858  * @class Roo.data.JsonStore
19859  * Small helper class to make creating Stores for JSON data easier. <br/>
19860 <pre><code>
19861 var store = new Roo.data.JsonStore({
19862     url: 'get-images.php',
19863     root: 'images',
19864     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19865 });
19866 </code></pre>
19867  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19868  * JsonReader and HttpProxy (unless inline data is provided).</b>
19869  * @cfg {Array} fields An array of field definition objects, or field name strings.
19870  * @constructor
19871  * @param {Object} config
19872  */
19873 Roo.data.JsonStore = function(c){
19874     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19875         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19876         reader: new Roo.data.JsonReader(c, c.fields)
19877     }));
19878 };
19879 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19880  * Based on:
19881  * Ext JS Library 1.1.1
19882  * Copyright(c) 2006-2007, Ext JS, LLC.
19883  *
19884  * Originally Released Under LGPL - original licence link has changed is not relivant.
19885  *
19886  * Fork - LGPL
19887  * <script type="text/javascript">
19888  */
19889
19890  
19891 Roo.data.Field = function(config){
19892     if(typeof config == "string"){
19893         config = {name: config};
19894     }
19895     Roo.apply(this, config);
19896     
19897     if(!this.type){
19898         this.type = "auto";
19899     }
19900     
19901     var st = Roo.data.SortTypes;
19902     // named sortTypes are supported, here we look them up
19903     if(typeof this.sortType == "string"){
19904         this.sortType = st[this.sortType];
19905     }
19906     
19907     // set default sortType for strings and dates
19908     if(!this.sortType){
19909         switch(this.type){
19910             case "string":
19911                 this.sortType = st.asUCString;
19912                 break;
19913             case "date":
19914                 this.sortType = st.asDate;
19915                 break;
19916             default:
19917                 this.sortType = st.none;
19918         }
19919     }
19920
19921     // define once
19922     var stripRe = /[\$,%]/g;
19923
19924     // prebuilt conversion function for this field, instead of
19925     // switching every time we're reading a value
19926     if(!this.convert){
19927         var cv, dateFormat = this.dateFormat;
19928         switch(this.type){
19929             case "":
19930             case "auto":
19931             case undefined:
19932                 cv = function(v){ return v; };
19933                 break;
19934             case "string":
19935                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19936                 break;
19937             case "int":
19938                 cv = function(v){
19939                     return v !== undefined && v !== null && v !== '' ?
19940                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19941                     };
19942                 break;
19943             case "float":
19944                 cv = function(v){
19945                     return v !== undefined && v !== null && v !== '' ?
19946                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19947                     };
19948                 break;
19949             case "bool":
19950             case "boolean":
19951                 cv = function(v){ return v === true || v === "true" || v == 1; };
19952                 break;
19953             case "date":
19954                 cv = function(v){
19955                     if(!v){
19956                         return '';
19957                     }
19958                     if(v instanceof Date){
19959                         return v;
19960                     }
19961                     if(dateFormat){
19962                         if(dateFormat == "timestamp"){
19963                             return new Date(v*1000);
19964                         }
19965                         return Date.parseDate(v, dateFormat);
19966                     }
19967                     var parsed = Date.parse(v);
19968                     return parsed ? new Date(parsed) : null;
19969                 };
19970              break;
19971             
19972         }
19973         this.convert = cv;
19974     }
19975 };
19976
19977 Roo.data.Field.prototype = {
19978     dateFormat: null,
19979     defaultValue: "",
19980     mapping: null,
19981     sortType : null,
19982     sortDir : "ASC"
19983 };/*
19984  * Based on:
19985  * Ext JS Library 1.1.1
19986  * Copyright(c) 2006-2007, Ext JS, LLC.
19987  *
19988  * Originally Released Under LGPL - original licence link has changed is not relivant.
19989  *
19990  * Fork - LGPL
19991  * <script type="text/javascript">
19992  */
19993  
19994 // Base class for reading structured data from a data source.  This class is intended to be
19995 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19996
19997 /**
19998  * @class Roo.data.DataReader
19999  * Base class for reading structured data from a data source.  This class is intended to be
20000  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20001  */
20002
20003 Roo.data.DataReader = function(meta, recordType){
20004     
20005     this.meta = meta;
20006     
20007     this.recordType = recordType instanceof Array ? 
20008         Roo.data.Record.create(recordType) : recordType;
20009 };
20010
20011 Roo.data.DataReader.prototype = {
20012      /**
20013      * Create an empty record
20014      * @param {Object} data (optional) - overlay some values
20015      * @return {Roo.data.Record} record created.
20016      */
20017     newRow :  function(d) {
20018         var da =  {};
20019         this.recordType.prototype.fields.each(function(c) {
20020             switch( c.type) {
20021                 case 'int' : da[c.name] = 0; break;
20022                 case 'date' : da[c.name] = new Date(); break;
20023                 case 'float' : da[c.name] = 0.0; break;
20024                 case 'boolean' : da[c.name] = false; break;
20025                 default : da[c.name] = ""; break;
20026             }
20027             
20028         });
20029         return new this.recordType(Roo.apply(da, d));
20030     }
20031     
20032 };/*
20033  * Based on:
20034  * Ext JS Library 1.1.1
20035  * Copyright(c) 2006-2007, Ext JS, LLC.
20036  *
20037  * Originally Released Under LGPL - original licence link has changed is not relivant.
20038  *
20039  * Fork - LGPL
20040  * <script type="text/javascript">
20041  */
20042
20043 /**
20044  * @class Roo.data.DataProxy
20045  * @extends Roo.data.Observable
20046  * This class is an abstract base class for implementations which provide retrieval of
20047  * unformatted data objects.<br>
20048  * <p>
20049  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20050  * (of the appropriate type which knows how to parse the data object) to provide a block of
20051  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20052  * <p>
20053  * Custom implementations must implement the load method as described in
20054  * {@link Roo.data.HttpProxy#load}.
20055  */
20056 Roo.data.DataProxy = function(){
20057     this.addEvents({
20058         /**
20059          * @event beforeload
20060          * Fires before a network request is made to retrieve a data object.
20061          * @param {Object} This DataProxy object.
20062          * @param {Object} params The params parameter to the load function.
20063          */
20064         beforeload : true,
20065         /**
20066          * @event load
20067          * Fires before the load method's callback is called.
20068          * @param {Object} This DataProxy object.
20069          * @param {Object} o The data object.
20070          * @param {Object} arg The callback argument object passed to the load function.
20071          */
20072         load : true,
20073         /**
20074          * @event loadexception
20075          * Fires if an Exception occurs during data retrieval.
20076          * @param {Object} This DataProxy object.
20077          * @param {Object} o The data object.
20078          * @param {Object} arg The callback argument object passed to the load function.
20079          * @param {Object} e The Exception.
20080          */
20081         loadexception : true
20082     });
20083     Roo.data.DataProxy.superclass.constructor.call(this);
20084 };
20085
20086 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20087
20088     /**
20089      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20090      */
20091 /*
20092  * Based on:
20093  * Ext JS Library 1.1.1
20094  * Copyright(c) 2006-2007, Ext JS, LLC.
20095  *
20096  * Originally Released Under LGPL - original licence link has changed is not relivant.
20097  *
20098  * Fork - LGPL
20099  * <script type="text/javascript">
20100  */
20101 /**
20102  * @class Roo.data.MemoryProxy
20103  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20104  * to the Reader when its load method is called.
20105  * @constructor
20106  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20107  */
20108 Roo.data.MemoryProxy = function(data){
20109     if (data.data) {
20110         data = data.data;
20111     }
20112     Roo.data.MemoryProxy.superclass.constructor.call(this);
20113     this.data = data;
20114 };
20115
20116 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20117     /**
20118      * Load data from the requested source (in this case an in-memory
20119      * data object passed to the constructor), read the data object into
20120      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20121      * process that block using the passed callback.
20122      * @param {Object} params This parameter is not used by the MemoryProxy class.
20123      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20124      * object into a block of Roo.data.Records.
20125      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20126      * The function must be passed <ul>
20127      * <li>The Record block object</li>
20128      * <li>The "arg" argument from the load function</li>
20129      * <li>A boolean success indicator</li>
20130      * </ul>
20131      * @param {Object} scope The scope in which to call the callback
20132      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20133      */
20134     load : function(params, reader, callback, scope, arg){
20135         params = params || {};
20136         var result;
20137         try {
20138             result = reader.readRecords(this.data);
20139         }catch(e){
20140             this.fireEvent("loadexception", this, arg, null, e);
20141             callback.call(scope, null, arg, false);
20142             return;
20143         }
20144         callback.call(scope, result, arg, true);
20145     },
20146     
20147     // private
20148     update : function(params, records){
20149         
20150     }
20151 });/*
20152  * Based on:
20153  * Ext JS Library 1.1.1
20154  * Copyright(c) 2006-2007, Ext JS, LLC.
20155  *
20156  * Originally Released Under LGPL - original licence link has changed is not relivant.
20157  *
20158  * Fork - LGPL
20159  * <script type="text/javascript">
20160  */
20161 /**
20162  * @class Roo.data.HttpProxy
20163  * @extends Roo.data.DataProxy
20164  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20165  * configured to reference a certain URL.<br><br>
20166  * <p>
20167  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20168  * from which the running page was served.<br><br>
20169  * <p>
20170  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20171  * <p>
20172  * Be aware that to enable the browser to parse an XML document, the server must set
20173  * the Content-Type header in the HTTP response to "text/xml".
20174  * @constructor
20175  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20176  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20177  * will be used to make the request.
20178  */
20179 Roo.data.HttpProxy = function(conn){
20180     Roo.data.HttpProxy.superclass.constructor.call(this);
20181     // is conn a conn config or a real conn?
20182     this.conn = conn;
20183     this.useAjax = !conn || !conn.events;
20184   
20185 };
20186
20187 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20188     // thse are take from connection...
20189     
20190     /**
20191      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20192      */
20193     /**
20194      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20195      * extra parameters to each request made by this object. (defaults to undefined)
20196      */
20197     /**
20198      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20199      *  to each request made by this object. (defaults to undefined)
20200      */
20201     /**
20202      * @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)
20203      */
20204     /**
20205      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20206      */
20207      /**
20208      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20209      * @type Boolean
20210      */
20211   
20212
20213     /**
20214      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20215      * @type Boolean
20216      */
20217     /**
20218      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20219      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20220      * a finer-grained basis than the DataProxy events.
20221      */
20222     getConnection : function(){
20223         return this.useAjax ? Roo.Ajax : this.conn;
20224     },
20225
20226     /**
20227      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20228      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20229      * process that block using the passed callback.
20230      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20231      * for the request to the remote server.
20232      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20233      * object into a block of Roo.data.Records.
20234      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20235      * The function must be passed <ul>
20236      * <li>The Record block object</li>
20237      * <li>The "arg" argument from the load function</li>
20238      * <li>A boolean success indicator</li>
20239      * </ul>
20240      * @param {Object} scope The scope in which to call the callback
20241      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20242      */
20243     load : function(params, reader, callback, scope, arg){
20244         if(this.fireEvent("beforeload", this, params) !== false){
20245             var  o = {
20246                 params : params || {},
20247                 request: {
20248                     callback : callback,
20249                     scope : scope,
20250                     arg : arg
20251                 },
20252                 reader: reader,
20253                 callback : this.loadResponse,
20254                 scope: this
20255             };
20256             if(this.useAjax){
20257                 Roo.applyIf(o, this.conn);
20258                 if(this.activeRequest){
20259                     Roo.Ajax.abort(this.activeRequest);
20260                 }
20261                 this.activeRequest = Roo.Ajax.request(o);
20262             }else{
20263                 this.conn.request(o);
20264             }
20265         }else{
20266             callback.call(scope||this, null, arg, false);
20267         }
20268     },
20269
20270     // private
20271     loadResponse : function(o, success, response){
20272         delete this.activeRequest;
20273         if(!success){
20274             this.fireEvent("loadexception", this, o, response);
20275             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20276             return;
20277         }
20278         var result;
20279         try {
20280             result = o.reader.read(response);
20281         }catch(e){
20282             this.fireEvent("loadexception", this, o, response, e);
20283             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20284             return;
20285         }
20286         
20287         this.fireEvent("load", this, o, o.request.arg);
20288         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20289     },
20290
20291     // private
20292     update : function(dataSet){
20293
20294     },
20295
20296     // private
20297     updateResponse : function(dataSet){
20298
20299     }
20300 });/*
20301  * Based on:
20302  * Ext JS Library 1.1.1
20303  * Copyright(c) 2006-2007, Ext JS, LLC.
20304  *
20305  * Originally Released Under LGPL - original licence link has changed is not relivant.
20306  *
20307  * Fork - LGPL
20308  * <script type="text/javascript">
20309  */
20310
20311 /**
20312  * @class Roo.data.ScriptTagProxy
20313  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20314  * other than the originating domain of the running page.<br><br>
20315  * <p>
20316  * <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
20317  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20318  * <p>
20319  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20320  * source code that is used as the source inside a &lt;script> tag.<br><br>
20321  * <p>
20322  * In order for the browser to process the returned data, the server must wrap the data object
20323  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20324  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20325  * depending on whether the callback name was passed:
20326  * <p>
20327  * <pre><code>
20328 boolean scriptTag = false;
20329 String cb = request.getParameter("callback");
20330 if (cb != null) {
20331     scriptTag = true;
20332     response.setContentType("text/javascript");
20333 } else {
20334     response.setContentType("application/x-json");
20335 }
20336 Writer out = response.getWriter();
20337 if (scriptTag) {
20338     out.write(cb + "(");
20339 }
20340 out.print(dataBlock.toJsonString());
20341 if (scriptTag) {
20342     out.write(");");
20343 }
20344 </pre></code>
20345  *
20346  * @constructor
20347  * @param {Object} config A configuration object.
20348  */
20349 Roo.data.ScriptTagProxy = function(config){
20350     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20351     Roo.apply(this, config);
20352     this.head = document.getElementsByTagName("head")[0];
20353 };
20354
20355 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20356
20357 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20358     /**
20359      * @cfg {String} url The URL from which to request the data object.
20360      */
20361     /**
20362      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20363      */
20364     timeout : 30000,
20365     /**
20366      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20367      * the server the name of the callback function set up by the load call to process the returned data object.
20368      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20369      * javascript output which calls this named function passing the data object as its only parameter.
20370      */
20371     callbackParam : "callback",
20372     /**
20373      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20374      * name to the request.
20375      */
20376     nocache : true,
20377
20378     /**
20379      * Load data from the configured URL, read the data object into
20380      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20381      * process that block using the passed callback.
20382      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20383      * for the request to the remote server.
20384      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20385      * object into a block of Roo.data.Records.
20386      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20387      * The function must be passed <ul>
20388      * <li>The Record block object</li>
20389      * <li>The "arg" argument from the load function</li>
20390      * <li>A boolean success indicator</li>
20391      * </ul>
20392      * @param {Object} scope The scope in which to call the callback
20393      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20394      */
20395     load : function(params, reader, callback, scope, arg){
20396         if(this.fireEvent("beforeload", this, params) !== false){
20397
20398             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20399
20400             var url = this.url;
20401             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20402             if(this.nocache){
20403                 url += "&_dc=" + (new Date().getTime());
20404             }
20405             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20406             var trans = {
20407                 id : transId,
20408                 cb : "stcCallback"+transId,
20409                 scriptId : "stcScript"+transId,
20410                 params : params,
20411                 arg : arg,
20412                 url : url,
20413                 callback : callback,
20414                 scope : scope,
20415                 reader : reader
20416             };
20417             var conn = this;
20418
20419             window[trans.cb] = function(o){
20420                 conn.handleResponse(o, trans);
20421             };
20422
20423             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20424
20425             if(this.autoAbort !== false){
20426                 this.abort();
20427             }
20428
20429             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20430
20431             var script = document.createElement("script");
20432             script.setAttribute("src", url);
20433             script.setAttribute("type", "text/javascript");
20434             script.setAttribute("id", trans.scriptId);
20435             this.head.appendChild(script);
20436
20437             this.trans = trans;
20438         }else{
20439             callback.call(scope||this, null, arg, false);
20440         }
20441     },
20442
20443     // private
20444     isLoading : function(){
20445         return this.trans ? true : false;
20446     },
20447
20448     /**
20449      * Abort the current server request.
20450      */
20451     abort : function(){
20452         if(this.isLoading()){
20453             this.destroyTrans(this.trans);
20454         }
20455     },
20456
20457     // private
20458     destroyTrans : function(trans, isLoaded){
20459         this.head.removeChild(document.getElementById(trans.scriptId));
20460         clearTimeout(trans.timeoutId);
20461         if(isLoaded){
20462             window[trans.cb] = undefined;
20463             try{
20464                 delete window[trans.cb];
20465             }catch(e){}
20466         }else{
20467             // if hasn't been loaded, wait for load to remove it to prevent script error
20468             window[trans.cb] = function(){
20469                 window[trans.cb] = undefined;
20470                 try{
20471                     delete window[trans.cb];
20472                 }catch(e){}
20473             };
20474         }
20475     },
20476
20477     // private
20478     handleResponse : function(o, trans){
20479         this.trans = false;
20480         this.destroyTrans(trans, true);
20481         var result;
20482         try {
20483             result = trans.reader.readRecords(o);
20484         }catch(e){
20485             this.fireEvent("loadexception", this, o, trans.arg, e);
20486             trans.callback.call(trans.scope||window, null, trans.arg, false);
20487             return;
20488         }
20489         this.fireEvent("load", this, o, trans.arg);
20490         trans.callback.call(trans.scope||window, result, trans.arg, true);
20491     },
20492
20493     // private
20494     handleFailure : function(trans){
20495         this.trans = false;
20496         this.destroyTrans(trans, false);
20497         this.fireEvent("loadexception", this, null, trans.arg);
20498         trans.callback.call(trans.scope||window, null, trans.arg, false);
20499     }
20500 });/*
20501  * Based on:
20502  * Ext JS Library 1.1.1
20503  * Copyright(c) 2006-2007, Ext JS, LLC.
20504  *
20505  * Originally Released Under LGPL - original licence link has changed is not relivant.
20506  *
20507  * Fork - LGPL
20508  * <script type="text/javascript">
20509  */
20510
20511 /**
20512  * @class Roo.data.JsonReader
20513  * @extends Roo.data.DataReader
20514  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20515  * based on mappings in a provided Roo.data.Record constructor.
20516  * 
20517  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20518  * in the reply previously. 
20519  * 
20520  * <p>
20521  * Example code:
20522  * <pre><code>
20523 var RecordDef = Roo.data.Record.create([
20524     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20525     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20526 ]);
20527 var myReader = new Roo.data.JsonReader({
20528     totalProperty: "results",    // The property which contains the total dataset size (optional)
20529     root: "rows",                // The property which contains an Array of row objects
20530     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20531 }, RecordDef);
20532 </code></pre>
20533  * <p>
20534  * This would consume a JSON file like this:
20535  * <pre><code>
20536 { 'results': 2, 'rows': [
20537     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20538     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20539 }
20540 </code></pre>
20541  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20542  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20543  * paged from the remote server.
20544  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20545  * @cfg {String} root name of the property which contains the Array of row objects.
20546  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20547  * @constructor
20548  * Create a new JsonReader
20549  * @param {Object} meta Metadata configuration options
20550  * @param {Object} recordType Either an Array of field definition objects,
20551  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20552  */
20553 Roo.data.JsonReader = function(meta, recordType){
20554     
20555     meta = meta || {};
20556     // set some defaults:
20557     Roo.applyIf(meta, {
20558         totalProperty: 'total',
20559         successProperty : 'success',
20560         root : 'data',
20561         id : 'id'
20562     });
20563     
20564     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20565 };
20566 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20567     
20568     /**
20569      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20570      * Used by Store query builder to append _requestMeta to params.
20571      * 
20572      */
20573     metaFromRemote : false,
20574     /**
20575      * This method is only used by a DataProxy which has retrieved data from a remote server.
20576      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20577      * @return {Object} data A data block which is used by an Roo.data.Store object as
20578      * a cache of Roo.data.Records.
20579      */
20580     read : function(response){
20581         var json = response.responseText;
20582        
20583         var o = /* eval:var:o */ eval("("+json+")");
20584         if(!o) {
20585             throw {message: "JsonReader.read: Json object not found"};
20586         }
20587         
20588         if(o.metaData){
20589             
20590             delete this.ef;
20591             this.metaFromRemote = true;
20592             this.meta = o.metaData;
20593             this.recordType = Roo.data.Record.create(o.metaData.fields);
20594             this.onMetaChange(this.meta, this.recordType, o);
20595         }
20596         return this.readRecords(o);
20597     },
20598
20599     // private function a store will implement
20600     onMetaChange : function(meta, recordType, o){
20601
20602     },
20603
20604     /**
20605          * @ignore
20606          */
20607     simpleAccess: function(obj, subsc) {
20608         return obj[subsc];
20609     },
20610
20611         /**
20612          * @ignore
20613          */
20614     getJsonAccessor: function(){
20615         var re = /[\[\.]/;
20616         return function(expr) {
20617             try {
20618                 return(re.test(expr))
20619                     ? new Function("obj", "return obj." + expr)
20620                     : function(obj){
20621                         return obj[expr];
20622                     };
20623             } catch(e){}
20624             return Roo.emptyFn;
20625         };
20626     }(),
20627
20628     /**
20629      * Create a data block containing Roo.data.Records from an XML document.
20630      * @param {Object} o An object which contains an Array of row objects in the property specified
20631      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20632      * which contains the total size of the dataset.
20633      * @return {Object} data A data block which is used by an Roo.data.Store object as
20634      * a cache of Roo.data.Records.
20635      */
20636     readRecords : function(o){
20637         /**
20638          * After any data loads, the raw JSON data is available for further custom processing.
20639          * @type Object
20640          */
20641         this.jsonData = o;
20642         var s = this.meta, Record = this.recordType,
20643             f = Record.prototype.fields, fi = f.items, fl = f.length;
20644
20645 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20646         if (!this.ef) {
20647             if(s.totalProperty) {
20648                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20649                 }
20650                 if(s.successProperty) {
20651                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20652                 }
20653                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20654                 if (s.id) {
20655                         var g = this.getJsonAccessor(s.id);
20656                         this.getId = function(rec) {
20657                                 var r = g(rec);
20658                                 return (r === undefined || r === "") ? null : r;
20659                         };
20660                 } else {
20661                         this.getId = function(){return null;};
20662                 }
20663             this.ef = [];
20664             for(var jj = 0; jj < fl; jj++){
20665                 f = fi[jj];
20666                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20667                 this.ef[jj] = this.getJsonAccessor(map);
20668             }
20669         }
20670
20671         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20672         if(s.totalProperty){
20673             var vt = parseInt(this.getTotal(o), 10);
20674             if(!isNaN(vt)){
20675                 totalRecords = vt;
20676             }
20677         }
20678         if(s.successProperty){
20679             var vs = this.getSuccess(o);
20680             if(vs === false || vs === 'false'){
20681                 success = false;
20682             }
20683         }
20684         var records = [];
20685             for(var i = 0; i < c; i++){
20686                     var n = root[i];
20687                 var values = {};
20688                 var id = this.getId(n);
20689                 for(var j = 0; j < fl; j++){
20690                     f = fi[j];
20691                 var v = this.ef[j](n);
20692                 if (!f.convert) {
20693                     Roo.log('missing convert for ' + f.name);
20694                     Roo.log(f);
20695                     continue;
20696                 }
20697                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20698                 }
20699                 var record = new Record(values, id);
20700                 record.json = n;
20701                 records[i] = record;
20702             }
20703             return {
20704                 success : success,
20705                 records : records,
20706                 totalRecords : totalRecords
20707             };
20708     }
20709 });/*
20710  * Based on:
20711  * Ext JS Library 1.1.1
20712  * Copyright(c) 2006-2007, Ext JS, LLC.
20713  *
20714  * Originally Released Under LGPL - original licence link has changed is not relivant.
20715  *
20716  * Fork - LGPL
20717  * <script type="text/javascript">
20718  */
20719
20720 /**
20721  * @class Roo.data.XmlReader
20722  * @extends Roo.data.DataReader
20723  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20724  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20725  * <p>
20726  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20727  * header in the HTTP response must be set to "text/xml".</em>
20728  * <p>
20729  * Example code:
20730  * <pre><code>
20731 var RecordDef = Roo.data.Record.create([
20732    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20733    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20734 ]);
20735 var myReader = new Roo.data.XmlReader({
20736    totalRecords: "results", // The element which contains the total dataset size (optional)
20737    record: "row",           // The repeated element which contains row information
20738    id: "id"                 // The element within the row that provides an ID for the record (optional)
20739 }, RecordDef);
20740 </code></pre>
20741  * <p>
20742  * This would consume an XML file like this:
20743  * <pre><code>
20744 &lt;?xml?>
20745 &lt;dataset>
20746  &lt;results>2&lt;/results>
20747  &lt;row>
20748    &lt;id>1&lt;/id>
20749    &lt;name>Bill&lt;/name>
20750    &lt;occupation>Gardener&lt;/occupation>
20751  &lt;/row>
20752  &lt;row>
20753    &lt;id>2&lt;/id>
20754    &lt;name>Ben&lt;/name>
20755    &lt;occupation>Horticulturalist&lt;/occupation>
20756  &lt;/row>
20757 &lt;/dataset>
20758 </code></pre>
20759  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20760  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20761  * paged from the remote server.
20762  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20763  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20764  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20765  * a record identifier value.
20766  * @constructor
20767  * Create a new XmlReader
20768  * @param {Object} meta Metadata configuration options
20769  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20770  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20771  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20772  */
20773 Roo.data.XmlReader = function(meta, recordType){
20774     meta = meta || {};
20775     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20776 };
20777 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20778     /**
20779      * This method is only used by a DataProxy which has retrieved data from a remote server.
20780          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20781          * to contain a method called 'responseXML' that returns an XML document object.
20782      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20783      * a cache of Roo.data.Records.
20784      */
20785     read : function(response){
20786         var doc = response.responseXML;
20787         if(!doc) {
20788             throw {message: "XmlReader.read: XML Document not available"};
20789         }
20790         return this.readRecords(doc);
20791     },
20792
20793     /**
20794      * Create a data block containing Roo.data.Records from an XML document.
20795          * @param {Object} doc A parsed XML document.
20796      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20797      * a cache of Roo.data.Records.
20798      */
20799     readRecords : function(doc){
20800         /**
20801          * After any data loads/reads, the raw XML Document is available for further custom processing.
20802          * @type XMLDocument
20803          */
20804         this.xmlData = doc;
20805         var root = doc.documentElement || doc;
20806         var q = Roo.DomQuery;
20807         var recordType = this.recordType, fields = recordType.prototype.fields;
20808         var sid = this.meta.id;
20809         var totalRecords = 0, success = true;
20810         if(this.meta.totalRecords){
20811             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20812         }
20813         
20814         if(this.meta.success){
20815             var sv = q.selectValue(this.meta.success, root, true);
20816             success = sv !== false && sv !== 'false';
20817         }
20818         var records = [];
20819         var ns = q.select(this.meta.record, root);
20820         for(var i = 0, len = ns.length; i < len; i++) {
20821                 var n = ns[i];
20822                 var values = {};
20823                 var id = sid ? q.selectValue(sid, n) : undefined;
20824                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20825                     var f = fields.items[j];
20826                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20827                     v = f.convert(v);
20828                     values[f.name] = v;
20829                 }
20830                 var record = new recordType(values, id);
20831                 record.node = n;
20832                 records[records.length] = record;
20833             }
20834
20835             return {
20836                 success : success,
20837                 records : records,
20838                 totalRecords : totalRecords || records.length
20839             };
20840     }
20841 });/*
20842  * Based on:
20843  * Ext JS Library 1.1.1
20844  * Copyright(c) 2006-2007, Ext JS, LLC.
20845  *
20846  * Originally Released Under LGPL - original licence link has changed is not relivant.
20847  *
20848  * Fork - LGPL
20849  * <script type="text/javascript">
20850  */
20851
20852 /**
20853  * @class Roo.data.ArrayReader
20854  * @extends Roo.data.DataReader
20855  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20856  * Each element of that Array represents a row of data fields. The
20857  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20858  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20859  * <p>
20860  * Example code:.
20861  * <pre><code>
20862 var RecordDef = Roo.data.Record.create([
20863     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20864     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20865 ]);
20866 var myReader = new Roo.data.ArrayReader({
20867     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20868 }, RecordDef);
20869 </code></pre>
20870  * <p>
20871  * This would consume an Array like this:
20872  * <pre><code>
20873 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20874   </code></pre>
20875  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20876  * @constructor
20877  * Create a new JsonReader
20878  * @param {Object} meta Metadata configuration options.
20879  * @param {Object} recordType Either an Array of field definition objects
20880  * as specified to {@link Roo.data.Record#create},
20881  * or an {@link Roo.data.Record} object
20882  * created using {@link Roo.data.Record#create}.
20883  */
20884 Roo.data.ArrayReader = function(meta, recordType){
20885     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20886 };
20887
20888 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20889     /**
20890      * Create a data block containing Roo.data.Records from an XML document.
20891      * @param {Object} o An Array of row objects which represents the dataset.
20892      * @return {Object} data A data block which is used by an Roo.data.Store object as
20893      * a cache of Roo.data.Records.
20894      */
20895     readRecords : function(o){
20896         var sid = this.meta ? this.meta.id : null;
20897         var recordType = this.recordType, fields = recordType.prototype.fields;
20898         var records = [];
20899         var root = o;
20900             for(var i = 0; i < root.length; i++){
20901                     var n = root[i];
20902                 var values = {};
20903                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20904                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20905                 var f = fields.items[j];
20906                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20907                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20908                 v = f.convert(v);
20909                 values[f.name] = v;
20910             }
20911                 var record = new recordType(values, id);
20912                 record.json = n;
20913                 records[records.length] = record;
20914             }
20915             return {
20916                 records : records,
20917                 totalRecords : records.length
20918             };
20919     }
20920 });/*
20921  * Based on:
20922  * Ext JS Library 1.1.1
20923  * Copyright(c) 2006-2007, Ext JS, LLC.
20924  *
20925  * Originally Released Under LGPL - original licence link has changed is not relivant.
20926  *
20927  * Fork - LGPL
20928  * <script type="text/javascript">
20929  */
20930
20931
20932 /**
20933  * @class Roo.data.Tree
20934  * @extends Roo.util.Observable
20935  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20936  * in the tree have most standard DOM functionality.
20937  * @constructor
20938  * @param {Node} root (optional) The root node
20939  */
20940 Roo.data.Tree = function(root){
20941    this.nodeHash = {};
20942    /**
20943     * The root node for this tree
20944     * @type Node
20945     */
20946    this.root = null;
20947    if(root){
20948        this.setRootNode(root);
20949    }
20950    this.addEvents({
20951        /**
20952         * @event append
20953         * Fires when a new child node is appended to a node in this tree.
20954         * @param {Tree} tree The owner tree
20955         * @param {Node} parent The parent node
20956         * @param {Node} node The newly appended node
20957         * @param {Number} index The index of the newly appended node
20958         */
20959        "append" : true,
20960        /**
20961         * @event remove
20962         * Fires when a child node is removed from a node in this tree.
20963         * @param {Tree} tree The owner tree
20964         * @param {Node} parent The parent node
20965         * @param {Node} node The child node removed
20966         */
20967        "remove" : true,
20968        /**
20969         * @event move
20970         * Fires when a node is moved to a new location in the tree
20971         * @param {Tree} tree The owner tree
20972         * @param {Node} node The node moved
20973         * @param {Node} oldParent The old parent of this node
20974         * @param {Node} newParent The new parent of this node
20975         * @param {Number} index The index it was moved to
20976         */
20977        "move" : true,
20978        /**
20979         * @event insert
20980         * Fires when a new child node is inserted in a node in this tree.
20981         * @param {Tree} tree The owner tree
20982         * @param {Node} parent The parent node
20983         * @param {Node} node The child node inserted
20984         * @param {Node} refNode The child node the node was inserted before
20985         */
20986        "insert" : true,
20987        /**
20988         * @event beforeappend
20989         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20990         * @param {Tree} tree The owner tree
20991         * @param {Node} parent The parent node
20992         * @param {Node} node The child node to be appended
20993         */
20994        "beforeappend" : true,
20995        /**
20996         * @event beforeremove
20997         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20998         * @param {Tree} tree The owner tree
20999         * @param {Node} parent The parent node
21000         * @param {Node} node The child node to be removed
21001         */
21002        "beforeremove" : true,
21003        /**
21004         * @event beforemove
21005         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21006         * @param {Tree} tree The owner tree
21007         * @param {Node} node The node being moved
21008         * @param {Node} oldParent The parent of the node
21009         * @param {Node} newParent The new parent the node is moving to
21010         * @param {Number} index The index it is being moved to
21011         */
21012        "beforemove" : true,
21013        /**
21014         * @event beforeinsert
21015         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21016         * @param {Tree} tree The owner tree
21017         * @param {Node} parent The parent node
21018         * @param {Node} node The child node to be inserted
21019         * @param {Node} refNode The child node the node is being inserted before
21020         */
21021        "beforeinsert" : true
21022    });
21023
21024     Roo.data.Tree.superclass.constructor.call(this);
21025 };
21026
21027 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21028     pathSeparator: "/",
21029
21030     proxyNodeEvent : function(){
21031         return this.fireEvent.apply(this, arguments);
21032     },
21033
21034     /**
21035      * Returns the root node for this tree.
21036      * @return {Node}
21037      */
21038     getRootNode : function(){
21039         return this.root;
21040     },
21041
21042     /**
21043      * Sets the root node for this tree.
21044      * @param {Node} node
21045      * @return {Node}
21046      */
21047     setRootNode : function(node){
21048         this.root = node;
21049         node.ownerTree = this;
21050         node.isRoot = true;
21051         this.registerNode(node);
21052         return node;
21053     },
21054
21055     /**
21056      * Gets a node in this tree by its id.
21057      * @param {String} id
21058      * @return {Node}
21059      */
21060     getNodeById : function(id){
21061         return this.nodeHash[id];
21062     },
21063
21064     registerNode : function(node){
21065         this.nodeHash[node.id] = node;
21066     },
21067
21068     unregisterNode : function(node){
21069         delete this.nodeHash[node.id];
21070     },
21071
21072     toString : function(){
21073         return "[Tree"+(this.id?" "+this.id:"")+"]";
21074     }
21075 });
21076
21077 /**
21078  * @class Roo.data.Node
21079  * @extends Roo.util.Observable
21080  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21081  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21082  * @constructor
21083  * @param {Object} attributes The attributes/config for the node
21084  */
21085 Roo.data.Node = function(attributes){
21086     /**
21087      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21088      * @type {Object}
21089      */
21090     this.attributes = attributes || {};
21091     this.leaf = this.attributes.leaf;
21092     /**
21093      * The node id. @type String
21094      */
21095     this.id = this.attributes.id;
21096     if(!this.id){
21097         this.id = Roo.id(null, "ynode-");
21098         this.attributes.id = this.id;
21099     }
21100     /**
21101      * All child nodes of this node. @type Array
21102      */
21103     this.childNodes = [];
21104     if(!this.childNodes.indexOf){ // indexOf is a must
21105         this.childNodes.indexOf = function(o){
21106             for(var i = 0, len = this.length; i < len; i++){
21107                 if(this[i] == o) {
21108                     return i;
21109                 }
21110             }
21111             return -1;
21112         };
21113     }
21114     /**
21115      * The parent node for this node. @type Node
21116      */
21117     this.parentNode = null;
21118     /**
21119      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21120      */
21121     this.firstChild = null;
21122     /**
21123      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21124      */
21125     this.lastChild = null;
21126     /**
21127      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21128      */
21129     this.previousSibling = null;
21130     /**
21131      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21132      */
21133     this.nextSibling = null;
21134
21135     this.addEvents({
21136        /**
21137         * @event append
21138         * Fires when a new child node is appended
21139         * @param {Tree} tree The owner tree
21140         * @param {Node} this This node
21141         * @param {Node} node The newly appended node
21142         * @param {Number} index The index of the newly appended node
21143         */
21144        "append" : true,
21145        /**
21146         * @event remove
21147         * Fires when a child node is removed
21148         * @param {Tree} tree The owner tree
21149         * @param {Node} this This node
21150         * @param {Node} node The removed node
21151         */
21152        "remove" : true,
21153        /**
21154         * @event move
21155         * Fires when this node is moved to a new location in the tree
21156         * @param {Tree} tree The owner tree
21157         * @param {Node} this This node
21158         * @param {Node} oldParent The old parent of this node
21159         * @param {Node} newParent The new parent of this node
21160         * @param {Number} index The index it was moved to
21161         */
21162        "move" : true,
21163        /**
21164         * @event insert
21165         * Fires when a new child node is inserted.
21166         * @param {Tree} tree The owner tree
21167         * @param {Node} this This node
21168         * @param {Node} node The child node inserted
21169         * @param {Node} refNode The child node the node was inserted before
21170         */
21171        "insert" : true,
21172        /**
21173         * @event beforeappend
21174         * Fires before a new child is appended, return false to cancel the append.
21175         * @param {Tree} tree The owner tree
21176         * @param {Node} this This node
21177         * @param {Node} node The child node to be appended
21178         */
21179        "beforeappend" : true,
21180        /**
21181         * @event beforeremove
21182         * Fires before a child is removed, return false to cancel the remove.
21183         * @param {Tree} tree The owner tree
21184         * @param {Node} this This node
21185         * @param {Node} node The child node to be removed
21186         */
21187        "beforeremove" : true,
21188        /**
21189         * @event beforemove
21190         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21191         * @param {Tree} tree The owner tree
21192         * @param {Node} this This node
21193         * @param {Node} oldParent The parent of this node
21194         * @param {Node} newParent The new parent this node is moving to
21195         * @param {Number} index The index it is being moved to
21196         */
21197        "beforemove" : true,
21198        /**
21199         * @event beforeinsert
21200         * Fires before a new child is inserted, return false to cancel the insert.
21201         * @param {Tree} tree The owner tree
21202         * @param {Node} this This node
21203         * @param {Node} node The child node to be inserted
21204         * @param {Node} refNode The child node the node is being inserted before
21205         */
21206        "beforeinsert" : true
21207    });
21208     this.listeners = this.attributes.listeners;
21209     Roo.data.Node.superclass.constructor.call(this);
21210 };
21211
21212 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21213     fireEvent : function(evtName){
21214         // first do standard event for this node
21215         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21216             return false;
21217         }
21218         // then bubble it up to the tree if the event wasn't cancelled
21219         var ot = this.getOwnerTree();
21220         if(ot){
21221             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21222                 return false;
21223             }
21224         }
21225         return true;
21226     },
21227
21228     /**
21229      * Returns true if this node is a leaf
21230      * @return {Boolean}
21231      */
21232     isLeaf : function(){
21233         return this.leaf === true;
21234     },
21235
21236     // private
21237     setFirstChild : function(node){
21238         this.firstChild = node;
21239     },
21240
21241     //private
21242     setLastChild : function(node){
21243         this.lastChild = node;
21244     },
21245
21246
21247     /**
21248      * Returns true if this node is the last child of its parent
21249      * @return {Boolean}
21250      */
21251     isLast : function(){
21252        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21253     },
21254
21255     /**
21256      * Returns true if this node is the first child of its parent
21257      * @return {Boolean}
21258      */
21259     isFirst : function(){
21260        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21261     },
21262
21263     hasChildNodes : function(){
21264         return !this.isLeaf() && this.childNodes.length > 0;
21265     },
21266
21267     /**
21268      * Insert node(s) as the last child node of this node.
21269      * @param {Node/Array} node The node or Array of nodes to append
21270      * @return {Node} The appended node if single append, or null if an array was passed
21271      */
21272     appendChild : function(node){
21273         var multi = false;
21274         if(node instanceof Array){
21275             multi = node;
21276         }else if(arguments.length > 1){
21277             multi = arguments;
21278         }
21279         // if passed an array or multiple args do them one by one
21280         if(multi){
21281             for(var i = 0, len = multi.length; i < len; i++) {
21282                 this.appendChild(multi[i]);
21283             }
21284         }else{
21285             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21286                 return false;
21287             }
21288             var index = this.childNodes.length;
21289             var oldParent = node.parentNode;
21290             // it's a move, make sure we move it cleanly
21291             if(oldParent){
21292                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21293                     return false;
21294                 }
21295                 oldParent.removeChild(node);
21296             }
21297             index = this.childNodes.length;
21298             if(index == 0){
21299                 this.setFirstChild(node);
21300             }
21301             this.childNodes.push(node);
21302             node.parentNode = this;
21303             var ps = this.childNodes[index-1];
21304             if(ps){
21305                 node.previousSibling = ps;
21306                 ps.nextSibling = node;
21307             }else{
21308                 node.previousSibling = null;
21309             }
21310             node.nextSibling = null;
21311             this.setLastChild(node);
21312             node.setOwnerTree(this.getOwnerTree());
21313             this.fireEvent("append", this.ownerTree, this, node, index);
21314             if(oldParent){
21315                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21316             }
21317             return node;
21318         }
21319     },
21320
21321     /**
21322      * Removes a child node from this node.
21323      * @param {Node} node The node to remove
21324      * @return {Node} The removed node
21325      */
21326     removeChild : function(node){
21327         var index = this.childNodes.indexOf(node);
21328         if(index == -1){
21329             return false;
21330         }
21331         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21332             return false;
21333         }
21334
21335         // remove it from childNodes collection
21336         this.childNodes.splice(index, 1);
21337
21338         // update siblings
21339         if(node.previousSibling){
21340             node.previousSibling.nextSibling = node.nextSibling;
21341         }
21342         if(node.nextSibling){
21343             node.nextSibling.previousSibling = node.previousSibling;
21344         }
21345
21346         // update child refs
21347         if(this.firstChild == node){
21348             this.setFirstChild(node.nextSibling);
21349         }
21350         if(this.lastChild == node){
21351             this.setLastChild(node.previousSibling);
21352         }
21353
21354         node.setOwnerTree(null);
21355         // clear any references from the node
21356         node.parentNode = null;
21357         node.previousSibling = null;
21358         node.nextSibling = null;
21359         this.fireEvent("remove", this.ownerTree, this, node);
21360         return node;
21361     },
21362
21363     /**
21364      * Inserts the first node before the second node in this nodes childNodes collection.
21365      * @param {Node} node The node to insert
21366      * @param {Node} refNode The node to insert before (if null the node is appended)
21367      * @return {Node} The inserted node
21368      */
21369     insertBefore : function(node, refNode){
21370         if(!refNode){ // like standard Dom, refNode can be null for append
21371             return this.appendChild(node);
21372         }
21373         // nothing to do
21374         if(node == refNode){
21375             return false;
21376         }
21377
21378         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21379             return false;
21380         }
21381         var index = this.childNodes.indexOf(refNode);
21382         var oldParent = node.parentNode;
21383         var refIndex = index;
21384
21385         // when moving internally, indexes will change after remove
21386         if(oldParent == this && this.childNodes.indexOf(node) < index){
21387             refIndex--;
21388         }
21389
21390         // it's a move, make sure we move it cleanly
21391         if(oldParent){
21392             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21393                 return false;
21394             }
21395             oldParent.removeChild(node);
21396         }
21397         if(refIndex == 0){
21398             this.setFirstChild(node);
21399         }
21400         this.childNodes.splice(refIndex, 0, node);
21401         node.parentNode = this;
21402         var ps = this.childNodes[refIndex-1];
21403         if(ps){
21404             node.previousSibling = ps;
21405             ps.nextSibling = node;
21406         }else{
21407             node.previousSibling = null;
21408         }
21409         node.nextSibling = refNode;
21410         refNode.previousSibling = node;
21411         node.setOwnerTree(this.getOwnerTree());
21412         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21413         if(oldParent){
21414             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21415         }
21416         return node;
21417     },
21418
21419     /**
21420      * Returns the child node at the specified index.
21421      * @param {Number} index
21422      * @return {Node}
21423      */
21424     item : function(index){
21425         return this.childNodes[index];
21426     },
21427
21428     /**
21429      * Replaces one child node in this node with another.
21430      * @param {Node} newChild The replacement node
21431      * @param {Node} oldChild The node to replace
21432      * @return {Node} The replaced node
21433      */
21434     replaceChild : function(newChild, oldChild){
21435         this.insertBefore(newChild, oldChild);
21436         this.removeChild(oldChild);
21437         return oldChild;
21438     },
21439
21440     /**
21441      * Returns the index of a child node
21442      * @param {Node} node
21443      * @return {Number} The index of the node or -1 if it was not found
21444      */
21445     indexOf : function(child){
21446         return this.childNodes.indexOf(child);
21447     },
21448
21449     /**
21450      * Returns the tree this node is in.
21451      * @return {Tree}
21452      */
21453     getOwnerTree : function(){
21454         // if it doesn't have one, look for one
21455         if(!this.ownerTree){
21456             var p = this;
21457             while(p){
21458                 if(p.ownerTree){
21459                     this.ownerTree = p.ownerTree;
21460                     break;
21461                 }
21462                 p = p.parentNode;
21463             }
21464         }
21465         return this.ownerTree;
21466     },
21467
21468     /**
21469      * Returns depth of this node (the root node has a depth of 0)
21470      * @return {Number}
21471      */
21472     getDepth : function(){
21473         var depth = 0;
21474         var p = this;
21475         while(p.parentNode){
21476             ++depth;
21477             p = p.parentNode;
21478         }
21479         return depth;
21480     },
21481
21482     // private
21483     setOwnerTree : function(tree){
21484         // if it's move, we need to update everyone
21485         if(tree != this.ownerTree){
21486             if(this.ownerTree){
21487                 this.ownerTree.unregisterNode(this);
21488             }
21489             this.ownerTree = tree;
21490             var cs = this.childNodes;
21491             for(var i = 0, len = cs.length; i < len; i++) {
21492                 cs[i].setOwnerTree(tree);
21493             }
21494             if(tree){
21495                 tree.registerNode(this);
21496             }
21497         }
21498     },
21499
21500     /**
21501      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21502      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21503      * @return {String} The path
21504      */
21505     getPath : function(attr){
21506         attr = attr || "id";
21507         var p = this.parentNode;
21508         var b = [this.attributes[attr]];
21509         while(p){
21510             b.unshift(p.attributes[attr]);
21511             p = p.parentNode;
21512         }
21513         var sep = this.getOwnerTree().pathSeparator;
21514         return sep + b.join(sep);
21515     },
21516
21517     /**
21518      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21519      * function call will be the scope provided or the current node. The arguments to the function
21520      * will be the args provided or the current node. If the function returns false at any point,
21521      * the bubble is stopped.
21522      * @param {Function} fn The function to call
21523      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21524      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21525      */
21526     bubble : function(fn, scope, args){
21527         var p = this;
21528         while(p){
21529             if(fn.call(scope || p, args || p) === false){
21530                 break;
21531             }
21532             p = p.parentNode;
21533         }
21534     },
21535
21536     /**
21537      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21538      * function call will be the scope provided or the current node. The arguments to the function
21539      * will be the args provided or the current node. If the function returns false at any point,
21540      * the cascade is stopped on that branch.
21541      * @param {Function} fn The function to call
21542      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21543      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21544      */
21545     cascade : function(fn, scope, args){
21546         if(fn.call(scope || this, args || this) !== false){
21547             var cs = this.childNodes;
21548             for(var i = 0, len = cs.length; i < len; i++) {
21549                 cs[i].cascade(fn, scope, args);
21550             }
21551         }
21552     },
21553
21554     /**
21555      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21556      * function call will be the scope provided or the current node. The arguments to the function
21557      * will be the args provided or the current node. If the function returns false at any point,
21558      * the iteration stops.
21559      * @param {Function} fn The function to call
21560      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21561      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21562      */
21563     eachChild : function(fn, scope, args){
21564         var cs = this.childNodes;
21565         for(var i = 0, len = cs.length; i < len; i++) {
21566                 if(fn.call(scope || this, args || cs[i]) === false){
21567                     break;
21568                 }
21569         }
21570     },
21571
21572     /**
21573      * Finds the first child that has the attribute with the specified value.
21574      * @param {String} attribute The attribute name
21575      * @param {Mixed} value The value to search for
21576      * @return {Node} The found child or null if none was found
21577      */
21578     findChild : function(attribute, value){
21579         var cs = this.childNodes;
21580         for(var i = 0, len = cs.length; i < len; i++) {
21581                 if(cs[i].attributes[attribute] == value){
21582                     return cs[i];
21583                 }
21584         }
21585         return null;
21586     },
21587
21588     /**
21589      * Finds the first child by a custom function. The child matches if the function passed
21590      * returns true.
21591      * @param {Function} fn
21592      * @param {Object} scope (optional)
21593      * @return {Node} The found child or null if none was found
21594      */
21595     findChildBy : function(fn, scope){
21596         var cs = this.childNodes;
21597         for(var i = 0, len = cs.length; i < len; i++) {
21598                 if(fn.call(scope||cs[i], cs[i]) === true){
21599                     return cs[i];
21600                 }
21601         }
21602         return null;
21603     },
21604
21605     /**
21606      * Sorts this nodes children using the supplied sort function
21607      * @param {Function} fn
21608      * @param {Object} scope (optional)
21609      */
21610     sort : function(fn, scope){
21611         var cs = this.childNodes;
21612         var len = cs.length;
21613         if(len > 0){
21614             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21615             cs.sort(sortFn);
21616             for(var i = 0; i < len; i++){
21617                 var n = cs[i];
21618                 n.previousSibling = cs[i-1];
21619                 n.nextSibling = cs[i+1];
21620                 if(i == 0){
21621                     this.setFirstChild(n);
21622                 }
21623                 if(i == len-1){
21624                     this.setLastChild(n);
21625                 }
21626             }
21627         }
21628     },
21629
21630     /**
21631      * Returns true if this node is an ancestor (at any point) of the passed node.
21632      * @param {Node} node
21633      * @return {Boolean}
21634      */
21635     contains : function(node){
21636         return node.isAncestor(this);
21637     },
21638
21639     /**
21640      * Returns true if the passed node is an ancestor (at any point) of this node.
21641      * @param {Node} node
21642      * @return {Boolean}
21643      */
21644     isAncestor : function(node){
21645         var p = this.parentNode;
21646         while(p){
21647             if(p == node){
21648                 return true;
21649             }
21650             p = p.parentNode;
21651         }
21652         return false;
21653     },
21654
21655     toString : function(){
21656         return "[Node"+(this.id?" "+this.id:"")+"]";
21657     }
21658 });/*
21659  * Based on:
21660  * Ext JS Library 1.1.1
21661  * Copyright(c) 2006-2007, Ext JS, LLC.
21662  *
21663  * Originally Released Under LGPL - original licence link has changed is not relivant.
21664  *
21665  * Fork - LGPL
21666  * <script type="text/javascript">
21667  */
21668  
21669
21670 /**
21671  * @class Roo.ComponentMgr
21672  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21673  * @singleton
21674  */
21675 Roo.ComponentMgr = function(){
21676     var all = new Roo.util.MixedCollection();
21677
21678     return {
21679         /**
21680          * Registers a component.
21681          * @param {Roo.Component} c The component
21682          */
21683         register : function(c){
21684             all.add(c);
21685         },
21686
21687         /**
21688          * Unregisters a component.
21689          * @param {Roo.Component} c The component
21690          */
21691         unregister : function(c){
21692             all.remove(c);
21693         },
21694
21695         /**
21696          * Returns a component by id
21697          * @param {String} id The component id
21698          */
21699         get : function(id){
21700             return all.get(id);
21701         },
21702
21703         /**
21704          * Registers a function that will be called when a specified component is added to ComponentMgr
21705          * @param {String} id The component id
21706          * @param {Funtction} fn The callback function
21707          * @param {Object} scope The scope of the callback
21708          */
21709         onAvailable : function(id, fn, scope){
21710             all.on("add", function(index, o){
21711                 if(o.id == id){
21712                     fn.call(scope || o, o);
21713                     all.un("add", fn, scope);
21714                 }
21715             });
21716         }
21717     };
21718 }();/*
21719  * Based on:
21720  * Ext JS Library 1.1.1
21721  * Copyright(c) 2006-2007, Ext JS, LLC.
21722  *
21723  * Originally Released Under LGPL - original licence link has changed is not relivant.
21724  *
21725  * Fork - LGPL
21726  * <script type="text/javascript">
21727  */
21728  
21729 /**
21730  * @class Roo.Component
21731  * @extends Roo.util.Observable
21732  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21733  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21734  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21735  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21736  * All visual components (widgets) that require rendering into a layout should subclass Component.
21737  * @constructor
21738  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21739  * 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
21740  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21741  */
21742 Roo.Component = function(config){
21743     config = config || {};
21744     if(config.tagName || config.dom || typeof config == "string"){ // element object
21745         config = {el: config, id: config.id || config};
21746     }
21747     this.initialConfig = config;
21748
21749     Roo.apply(this, config);
21750     this.addEvents({
21751         /**
21752          * @event disable
21753          * Fires after the component is disabled.
21754              * @param {Roo.Component} this
21755              */
21756         disable : true,
21757         /**
21758          * @event enable
21759          * Fires after the component is enabled.
21760              * @param {Roo.Component} this
21761              */
21762         enable : true,
21763         /**
21764          * @event beforeshow
21765          * Fires before the component is shown.  Return false to stop the show.
21766              * @param {Roo.Component} this
21767              */
21768         beforeshow : true,
21769         /**
21770          * @event show
21771          * Fires after the component is shown.
21772              * @param {Roo.Component} this
21773              */
21774         show : true,
21775         /**
21776          * @event beforehide
21777          * Fires before the component is hidden. Return false to stop the hide.
21778              * @param {Roo.Component} this
21779              */
21780         beforehide : true,
21781         /**
21782          * @event hide
21783          * Fires after the component is hidden.
21784              * @param {Roo.Component} this
21785              */
21786         hide : true,
21787         /**
21788          * @event beforerender
21789          * Fires before the component is rendered. Return false to stop the render.
21790              * @param {Roo.Component} this
21791              */
21792         beforerender : true,
21793         /**
21794          * @event render
21795          * Fires after the component is rendered.
21796              * @param {Roo.Component} this
21797              */
21798         render : true,
21799         /**
21800          * @event beforedestroy
21801          * Fires before the component is destroyed. Return false to stop the destroy.
21802              * @param {Roo.Component} this
21803              */
21804         beforedestroy : true,
21805         /**
21806          * @event destroy
21807          * Fires after the component is destroyed.
21808              * @param {Roo.Component} this
21809              */
21810         destroy : true
21811     });
21812     if(!this.id){
21813         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21814     }
21815     Roo.ComponentMgr.register(this);
21816     Roo.Component.superclass.constructor.call(this);
21817     this.initComponent();
21818     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21819         this.render(this.renderTo);
21820         delete this.renderTo;
21821     }
21822 };
21823
21824 // private
21825 Roo.Component.AUTO_ID = 1000;
21826
21827 Roo.extend(Roo.Component, Roo.util.Observable, {
21828     /**
21829      * @property {Boolean} hidden
21830      * true if this component is hidden. Read-only.
21831      */
21832     hidden : false,
21833     /**
21834      * true if this component is disabled. Read-only.
21835      */
21836     disabled : false,
21837     /**
21838      * true if this component has been rendered. Read-only.
21839      */
21840     rendered : false,
21841     
21842     /** @cfg {String} disableClass
21843      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21844      */
21845     disabledClass : "x-item-disabled",
21846         /** @cfg {Boolean} allowDomMove
21847          * Whether the component can move the Dom node when rendering (defaults to true).
21848          */
21849     allowDomMove : true,
21850     /** @cfg {String} hideMode
21851      * How this component should hidden. Supported values are
21852      * "visibility" (css visibility), "offsets" (negative offset position) and
21853      * "display" (css display) - defaults to "display".
21854      */
21855     hideMode: 'display',
21856
21857     // private
21858     ctype : "Roo.Component",
21859
21860     /** @cfg {String} actionMode 
21861      * which property holds the element that used for  hide() / show() / disable() / enable()
21862      * default is 'el' 
21863      */
21864     actionMode : "el",
21865
21866     // private
21867     getActionEl : function(){
21868         return this[this.actionMode];
21869     },
21870
21871     initComponent : Roo.emptyFn,
21872     /**
21873      * If this is a lazy rendering component, render it to its container element.
21874      * @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.
21875      */
21876     render : function(container, position){
21877         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21878             if(!container && this.el){
21879                 this.el = Roo.get(this.el);
21880                 container = this.el.dom.parentNode;
21881                 this.allowDomMove = false;
21882             }
21883             this.container = Roo.get(container);
21884             this.rendered = true;
21885             if(position !== undefined){
21886                 if(typeof position == 'number'){
21887                     position = this.container.dom.childNodes[position];
21888                 }else{
21889                     position = Roo.getDom(position);
21890                 }
21891             }
21892             this.onRender(this.container, position || null);
21893             if(this.cls){
21894                 this.el.addClass(this.cls);
21895                 delete this.cls;
21896             }
21897             if(this.style){
21898                 this.el.applyStyles(this.style);
21899                 delete this.style;
21900             }
21901             this.fireEvent("render", this);
21902             this.afterRender(this.container);
21903             if(this.hidden){
21904                 this.hide();
21905             }
21906             if(this.disabled){
21907                 this.disable();
21908             }
21909         }
21910         return this;
21911     },
21912
21913     // private
21914     // default function is not really useful
21915     onRender : function(ct, position){
21916         if(this.el){
21917             this.el = Roo.get(this.el);
21918             if(this.allowDomMove !== false){
21919                 ct.dom.insertBefore(this.el.dom, position);
21920             }
21921         }
21922     },
21923
21924     // private
21925     getAutoCreate : function(){
21926         var cfg = typeof this.autoCreate == "object" ?
21927                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21928         if(this.id && !cfg.id){
21929             cfg.id = this.id;
21930         }
21931         return cfg;
21932     },
21933
21934     // private
21935     afterRender : Roo.emptyFn,
21936
21937     /**
21938      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21939      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21940      */
21941     destroy : function(){
21942         if(this.fireEvent("beforedestroy", this) !== false){
21943             this.purgeListeners();
21944             this.beforeDestroy();
21945             if(this.rendered){
21946                 this.el.removeAllListeners();
21947                 this.el.remove();
21948                 if(this.actionMode == "container"){
21949                     this.container.remove();
21950                 }
21951             }
21952             this.onDestroy();
21953             Roo.ComponentMgr.unregister(this);
21954             this.fireEvent("destroy", this);
21955         }
21956     },
21957
21958         // private
21959     beforeDestroy : function(){
21960
21961     },
21962
21963         // private
21964         onDestroy : function(){
21965
21966     },
21967
21968     /**
21969      * Returns the underlying {@link Roo.Element}.
21970      * @return {Roo.Element} The element
21971      */
21972     getEl : function(){
21973         return this.el;
21974     },
21975
21976     /**
21977      * Returns the id of this component.
21978      * @return {String}
21979      */
21980     getId : function(){
21981         return this.id;
21982     },
21983
21984     /**
21985      * Try to focus this component.
21986      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21987      * @return {Roo.Component} this
21988      */
21989     focus : function(selectText){
21990         if(this.rendered){
21991             this.el.focus();
21992             if(selectText === true){
21993                 this.el.dom.select();
21994             }
21995         }
21996         return this;
21997     },
21998
21999     // private
22000     blur : function(){
22001         if(this.rendered){
22002             this.el.blur();
22003         }
22004         return this;
22005     },
22006
22007     /**
22008      * Disable this component.
22009      * @return {Roo.Component} this
22010      */
22011     disable : function(){
22012         if(this.rendered){
22013             this.onDisable();
22014         }
22015         this.disabled = true;
22016         this.fireEvent("disable", this);
22017         return this;
22018     },
22019
22020         // private
22021     onDisable : function(){
22022         this.getActionEl().addClass(this.disabledClass);
22023         this.el.dom.disabled = true;
22024     },
22025
22026     /**
22027      * Enable this component.
22028      * @return {Roo.Component} this
22029      */
22030     enable : function(){
22031         if(this.rendered){
22032             this.onEnable();
22033         }
22034         this.disabled = false;
22035         this.fireEvent("enable", this);
22036         return this;
22037     },
22038
22039         // private
22040     onEnable : function(){
22041         this.getActionEl().removeClass(this.disabledClass);
22042         this.el.dom.disabled = false;
22043     },
22044
22045     /**
22046      * Convenience function for setting disabled/enabled by boolean.
22047      * @param {Boolean} disabled
22048      */
22049     setDisabled : function(disabled){
22050         this[disabled ? "disable" : "enable"]();
22051     },
22052
22053     /**
22054      * Show this component.
22055      * @return {Roo.Component} this
22056      */
22057     show: function(){
22058         if(this.fireEvent("beforeshow", this) !== false){
22059             this.hidden = false;
22060             if(this.rendered){
22061                 this.onShow();
22062             }
22063             this.fireEvent("show", this);
22064         }
22065         return this;
22066     },
22067
22068     // private
22069     onShow : function(){
22070         var ae = this.getActionEl();
22071         if(this.hideMode == 'visibility'){
22072             ae.dom.style.visibility = "visible";
22073         }else if(this.hideMode == 'offsets'){
22074             ae.removeClass('x-hidden');
22075         }else{
22076             ae.dom.style.display = "";
22077         }
22078     },
22079
22080     /**
22081      * Hide this component.
22082      * @return {Roo.Component} this
22083      */
22084     hide: function(){
22085         if(this.fireEvent("beforehide", this) !== false){
22086             this.hidden = true;
22087             if(this.rendered){
22088                 this.onHide();
22089             }
22090             this.fireEvent("hide", this);
22091         }
22092         return this;
22093     },
22094
22095     // private
22096     onHide : function(){
22097         var ae = this.getActionEl();
22098         if(this.hideMode == 'visibility'){
22099             ae.dom.style.visibility = "hidden";
22100         }else if(this.hideMode == 'offsets'){
22101             ae.addClass('x-hidden');
22102         }else{
22103             ae.dom.style.display = "none";
22104         }
22105     },
22106
22107     /**
22108      * Convenience function to hide or show this component by boolean.
22109      * @param {Boolean} visible True to show, false to hide
22110      * @return {Roo.Component} this
22111      */
22112     setVisible: function(visible){
22113         if(visible) {
22114             this.show();
22115         }else{
22116             this.hide();
22117         }
22118         return this;
22119     },
22120
22121     /**
22122      * Returns true if this component is visible.
22123      */
22124     isVisible : function(){
22125         return this.getActionEl().isVisible();
22126     },
22127
22128     cloneConfig : function(overrides){
22129         overrides = overrides || {};
22130         var id = overrides.id || Roo.id();
22131         var cfg = Roo.applyIf(overrides, this.initialConfig);
22132         cfg.id = id; // prevent dup id
22133         return new this.constructor(cfg);
22134     }
22135 });/*
22136  * Based on:
22137  * Ext JS Library 1.1.1
22138  * Copyright(c) 2006-2007, Ext JS, LLC.
22139  *
22140  * Originally Released Under LGPL - original licence link has changed is not relivant.
22141  *
22142  * Fork - LGPL
22143  * <script type="text/javascript">
22144  */
22145  (function(){ 
22146 /**
22147  * @class Roo.Layer
22148  * @extends Roo.Element
22149  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22150  * automatic maintaining of shadow/shim positions.
22151  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22152  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22153  * you can pass a string with a CSS class name. False turns off the shadow.
22154  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22155  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22156  * @cfg {String} cls CSS class to add to the element
22157  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22158  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22159  * @constructor
22160  * @param {Object} config An object with config options.
22161  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22162  */
22163
22164 Roo.Layer = function(config, existingEl){
22165     config = config || {};
22166     var dh = Roo.DomHelper;
22167     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22168     if(existingEl){
22169         this.dom = Roo.getDom(existingEl);
22170     }
22171     if(!this.dom){
22172         var o = config.dh || {tag: "div", cls: "x-layer"};
22173         this.dom = dh.append(pel, o);
22174     }
22175     if(config.cls){
22176         this.addClass(config.cls);
22177     }
22178     this.constrain = config.constrain !== false;
22179     this.visibilityMode = Roo.Element.VISIBILITY;
22180     if(config.id){
22181         this.id = this.dom.id = config.id;
22182     }else{
22183         this.id = Roo.id(this.dom);
22184     }
22185     this.zindex = config.zindex || this.getZIndex();
22186     this.position("absolute", this.zindex);
22187     if(config.shadow){
22188         this.shadowOffset = config.shadowOffset || 4;
22189         this.shadow = new Roo.Shadow({
22190             offset : this.shadowOffset,
22191             mode : config.shadow
22192         });
22193     }else{
22194         this.shadowOffset = 0;
22195     }
22196     this.useShim = config.shim !== false && Roo.useShims;
22197     this.useDisplay = config.useDisplay;
22198     this.hide();
22199 };
22200
22201 var supr = Roo.Element.prototype;
22202
22203 // shims are shared among layer to keep from having 100 iframes
22204 var shims = [];
22205
22206 Roo.extend(Roo.Layer, Roo.Element, {
22207
22208     getZIndex : function(){
22209         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22210     },
22211
22212     getShim : function(){
22213         if(!this.useShim){
22214             return null;
22215         }
22216         if(this.shim){
22217             return this.shim;
22218         }
22219         var shim = shims.shift();
22220         if(!shim){
22221             shim = this.createShim();
22222             shim.enableDisplayMode('block');
22223             shim.dom.style.display = 'none';
22224             shim.dom.style.visibility = 'visible';
22225         }
22226         var pn = this.dom.parentNode;
22227         if(shim.dom.parentNode != pn){
22228             pn.insertBefore(shim.dom, this.dom);
22229         }
22230         shim.setStyle('z-index', this.getZIndex()-2);
22231         this.shim = shim;
22232         return shim;
22233     },
22234
22235     hideShim : function(){
22236         if(this.shim){
22237             this.shim.setDisplayed(false);
22238             shims.push(this.shim);
22239             delete this.shim;
22240         }
22241     },
22242
22243     disableShadow : function(){
22244         if(this.shadow){
22245             this.shadowDisabled = true;
22246             this.shadow.hide();
22247             this.lastShadowOffset = this.shadowOffset;
22248             this.shadowOffset = 0;
22249         }
22250     },
22251
22252     enableShadow : function(show){
22253         if(this.shadow){
22254             this.shadowDisabled = false;
22255             this.shadowOffset = this.lastShadowOffset;
22256             delete this.lastShadowOffset;
22257             if(show){
22258                 this.sync(true);
22259             }
22260         }
22261     },
22262
22263     // private
22264     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22265     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22266     sync : function(doShow){
22267         var sw = this.shadow;
22268         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22269             var sh = this.getShim();
22270
22271             var w = this.getWidth(),
22272                 h = this.getHeight();
22273
22274             var l = this.getLeft(true),
22275                 t = this.getTop(true);
22276
22277             if(sw && !this.shadowDisabled){
22278                 if(doShow && !sw.isVisible()){
22279                     sw.show(this);
22280                 }else{
22281                     sw.realign(l, t, w, h);
22282                 }
22283                 if(sh){
22284                     if(doShow){
22285                        sh.show();
22286                     }
22287                     // fit the shim behind the shadow, so it is shimmed too
22288                     var a = sw.adjusts, s = sh.dom.style;
22289                     s.left = (Math.min(l, l+a.l))+"px";
22290                     s.top = (Math.min(t, t+a.t))+"px";
22291                     s.width = (w+a.w)+"px";
22292                     s.height = (h+a.h)+"px";
22293                 }
22294             }else if(sh){
22295                 if(doShow){
22296                    sh.show();
22297                 }
22298                 sh.setSize(w, h);
22299                 sh.setLeftTop(l, t);
22300             }
22301             
22302         }
22303     },
22304
22305     // private
22306     destroy : function(){
22307         this.hideShim();
22308         if(this.shadow){
22309             this.shadow.hide();
22310         }
22311         this.removeAllListeners();
22312         var pn = this.dom.parentNode;
22313         if(pn){
22314             pn.removeChild(this.dom);
22315         }
22316         Roo.Element.uncache(this.id);
22317     },
22318
22319     remove : function(){
22320         this.destroy();
22321     },
22322
22323     // private
22324     beginUpdate : function(){
22325         this.updating = true;
22326     },
22327
22328     // private
22329     endUpdate : function(){
22330         this.updating = false;
22331         this.sync(true);
22332     },
22333
22334     // private
22335     hideUnders : function(negOffset){
22336         if(this.shadow){
22337             this.shadow.hide();
22338         }
22339         this.hideShim();
22340     },
22341
22342     // private
22343     constrainXY : function(){
22344         if(this.constrain){
22345             var vw = Roo.lib.Dom.getViewWidth(),
22346                 vh = Roo.lib.Dom.getViewHeight();
22347             var s = Roo.get(document).getScroll();
22348
22349             var xy = this.getXY();
22350             var x = xy[0], y = xy[1];   
22351             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22352             // only move it if it needs it
22353             var moved = false;
22354             // first validate right/bottom
22355             if((x + w) > vw+s.left){
22356                 x = vw - w - this.shadowOffset;
22357                 moved = true;
22358             }
22359             if((y + h) > vh+s.top){
22360                 y = vh - h - this.shadowOffset;
22361                 moved = true;
22362             }
22363             // then make sure top/left isn't negative
22364             if(x < s.left){
22365                 x = s.left;
22366                 moved = true;
22367             }
22368             if(y < s.top){
22369                 y = s.top;
22370                 moved = true;
22371             }
22372             if(moved){
22373                 if(this.avoidY){
22374                     var ay = this.avoidY;
22375                     if(y <= ay && (y+h) >= ay){
22376                         y = ay-h-5;   
22377                     }
22378                 }
22379                 xy = [x, y];
22380                 this.storeXY(xy);
22381                 supr.setXY.call(this, xy);
22382                 this.sync();
22383             }
22384         }
22385     },
22386
22387     isVisible : function(){
22388         return this.visible;    
22389     },
22390
22391     // private
22392     showAction : function(){
22393         this.visible = true; // track visibility to prevent getStyle calls
22394         if(this.useDisplay === true){
22395             this.setDisplayed("");
22396         }else if(this.lastXY){
22397             supr.setXY.call(this, this.lastXY);
22398         }else if(this.lastLT){
22399             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22400         }
22401     },
22402
22403     // private
22404     hideAction : function(){
22405         this.visible = false;
22406         if(this.useDisplay === true){
22407             this.setDisplayed(false);
22408         }else{
22409             this.setLeftTop(-10000,-10000);
22410         }
22411     },
22412
22413     // overridden Element method
22414     setVisible : function(v, a, d, c, e){
22415         if(v){
22416             this.showAction();
22417         }
22418         if(a && v){
22419             var cb = function(){
22420                 this.sync(true);
22421                 if(c){
22422                     c();
22423                 }
22424             }.createDelegate(this);
22425             supr.setVisible.call(this, true, true, d, cb, e);
22426         }else{
22427             if(!v){
22428                 this.hideUnders(true);
22429             }
22430             var cb = c;
22431             if(a){
22432                 cb = function(){
22433                     this.hideAction();
22434                     if(c){
22435                         c();
22436                     }
22437                 }.createDelegate(this);
22438             }
22439             supr.setVisible.call(this, v, a, d, cb, e);
22440             if(v){
22441                 this.sync(true);
22442             }else if(!a){
22443                 this.hideAction();
22444             }
22445         }
22446     },
22447
22448     storeXY : function(xy){
22449         delete this.lastLT;
22450         this.lastXY = xy;
22451     },
22452
22453     storeLeftTop : function(left, top){
22454         delete this.lastXY;
22455         this.lastLT = [left, top];
22456     },
22457
22458     // private
22459     beforeFx : function(){
22460         this.beforeAction();
22461         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22462     },
22463
22464     // private
22465     afterFx : function(){
22466         Roo.Layer.superclass.afterFx.apply(this, arguments);
22467         this.sync(this.isVisible());
22468     },
22469
22470     // private
22471     beforeAction : function(){
22472         if(!this.updating && this.shadow){
22473             this.shadow.hide();
22474         }
22475     },
22476
22477     // overridden Element method
22478     setLeft : function(left){
22479         this.storeLeftTop(left, this.getTop(true));
22480         supr.setLeft.apply(this, arguments);
22481         this.sync();
22482     },
22483
22484     setTop : function(top){
22485         this.storeLeftTop(this.getLeft(true), top);
22486         supr.setTop.apply(this, arguments);
22487         this.sync();
22488     },
22489
22490     setLeftTop : function(left, top){
22491         this.storeLeftTop(left, top);
22492         supr.setLeftTop.apply(this, arguments);
22493         this.sync();
22494     },
22495
22496     setXY : function(xy, a, d, c, e){
22497         this.fixDisplay();
22498         this.beforeAction();
22499         this.storeXY(xy);
22500         var cb = this.createCB(c);
22501         supr.setXY.call(this, xy, a, d, cb, e);
22502         if(!a){
22503             cb();
22504         }
22505     },
22506
22507     // private
22508     createCB : function(c){
22509         var el = this;
22510         return function(){
22511             el.constrainXY();
22512             el.sync(true);
22513             if(c){
22514                 c();
22515             }
22516         };
22517     },
22518
22519     // overridden Element method
22520     setX : function(x, a, d, c, e){
22521         this.setXY([x, this.getY()], a, d, c, e);
22522     },
22523
22524     // overridden Element method
22525     setY : function(y, a, d, c, e){
22526         this.setXY([this.getX(), y], a, d, c, e);
22527     },
22528
22529     // overridden Element method
22530     setSize : function(w, h, a, d, c, e){
22531         this.beforeAction();
22532         var cb = this.createCB(c);
22533         supr.setSize.call(this, w, h, a, d, cb, e);
22534         if(!a){
22535             cb();
22536         }
22537     },
22538
22539     // overridden Element method
22540     setWidth : function(w, a, d, c, e){
22541         this.beforeAction();
22542         var cb = this.createCB(c);
22543         supr.setWidth.call(this, w, a, d, cb, e);
22544         if(!a){
22545             cb();
22546         }
22547     },
22548
22549     // overridden Element method
22550     setHeight : function(h, a, d, c, e){
22551         this.beforeAction();
22552         var cb = this.createCB(c);
22553         supr.setHeight.call(this, h, a, d, cb, e);
22554         if(!a){
22555             cb();
22556         }
22557     },
22558
22559     // overridden Element method
22560     setBounds : function(x, y, w, h, a, d, c, e){
22561         this.beforeAction();
22562         var cb = this.createCB(c);
22563         if(!a){
22564             this.storeXY([x, y]);
22565             supr.setXY.call(this, [x, y]);
22566             supr.setSize.call(this, w, h, a, d, cb, e);
22567             cb();
22568         }else{
22569             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22570         }
22571         return this;
22572     },
22573     
22574     /**
22575      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22576      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22577      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22578      * @param {Number} zindex The new z-index to set
22579      * @return {this} The Layer
22580      */
22581     setZIndex : function(zindex){
22582         this.zindex = zindex;
22583         this.setStyle("z-index", zindex + 2);
22584         if(this.shadow){
22585             this.shadow.setZIndex(zindex + 1);
22586         }
22587         if(this.shim){
22588             this.shim.setStyle("z-index", zindex);
22589         }
22590     }
22591 });
22592 })();/*
22593  * Based on:
22594  * Ext JS Library 1.1.1
22595  * Copyright(c) 2006-2007, Ext JS, LLC.
22596  *
22597  * Originally Released Under LGPL - original licence link has changed is not relivant.
22598  *
22599  * Fork - LGPL
22600  * <script type="text/javascript">
22601  */
22602
22603
22604 /**
22605  * @class Roo.Shadow
22606  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22607  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22608  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22609  * @constructor
22610  * Create a new Shadow
22611  * @param {Object} config The config object
22612  */
22613 Roo.Shadow = function(config){
22614     Roo.apply(this, config);
22615     if(typeof this.mode != "string"){
22616         this.mode = this.defaultMode;
22617     }
22618     var o = this.offset, a = {h: 0};
22619     var rad = Math.floor(this.offset/2);
22620     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22621         case "drop":
22622             a.w = 0;
22623             a.l = a.t = o;
22624             a.t -= 1;
22625             if(Roo.isIE){
22626                 a.l -= this.offset + rad;
22627                 a.t -= this.offset + rad;
22628                 a.w -= rad;
22629                 a.h -= rad;
22630                 a.t += 1;
22631             }
22632         break;
22633         case "sides":
22634             a.w = (o*2);
22635             a.l = -o;
22636             a.t = o-1;
22637             if(Roo.isIE){
22638                 a.l -= (this.offset - rad);
22639                 a.t -= this.offset + rad;
22640                 a.l += 1;
22641                 a.w -= (this.offset - rad)*2;
22642                 a.w -= rad + 1;
22643                 a.h -= 1;
22644             }
22645         break;
22646         case "frame":
22647             a.w = a.h = (o*2);
22648             a.l = a.t = -o;
22649             a.t += 1;
22650             a.h -= 2;
22651             if(Roo.isIE){
22652                 a.l -= (this.offset - rad);
22653                 a.t -= (this.offset - rad);
22654                 a.l += 1;
22655                 a.w -= (this.offset + rad + 1);
22656                 a.h -= (this.offset + rad);
22657                 a.h += 1;
22658             }
22659         break;
22660     };
22661
22662     this.adjusts = a;
22663 };
22664
22665 Roo.Shadow.prototype = {
22666     /**
22667      * @cfg {String} mode
22668      * The shadow display mode.  Supports the following options:<br />
22669      * sides: Shadow displays on both sides and bottom only<br />
22670      * frame: Shadow displays equally on all four sides<br />
22671      * drop: Traditional bottom-right drop shadow (default)
22672      */
22673     /**
22674      * @cfg {String} offset
22675      * The number of pixels to offset the shadow from the element (defaults to 4)
22676      */
22677     offset: 4,
22678
22679     // private
22680     defaultMode: "drop",
22681
22682     /**
22683      * Displays the shadow under the target element
22684      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22685      */
22686     show : function(target){
22687         target = Roo.get(target);
22688         if(!this.el){
22689             this.el = Roo.Shadow.Pool.pull();
22690             if(this.el.dom.nextSibling != target.dom){
22691                 this.el.insertBefore(target);
22692             }
22693         }
22694         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22695         if(Roo.isIE){
22696             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22697         }
22698         this.realign(
22699             target.getLeft(true),
22700             target.getTop(true),
22701             target.getWidth(),
22702             target.getHeight()
22703         );
22704         this.el.dom.style.display = "block";
22705     },
22706
22707     /**
22708      * Returns true if the shadow is visible, else false
22709      */
22710     isVisible : function(){
22711         return this.el ? true : false;  
22712     },
22713
22714     /**
22715      * Direct alignment when values are already available. Show must be called at least once before
22716      * calling this method to ensure it is initialized.
22717      * @param {Number} left The target element left position
22718      * @param {Number} top The target element top position
22719      * @param {Number} width The target element width
22720      * @param {Number} height The target element height
22721      */
22722     realign : function(l, t, w, h){
22723         if(!this.el){
22724             return;
22725         }
22726         var a = this.adjusts, d = this.el.dom, s = d.style;
22727         var iea = 0;
22728         s.left = (l+a.l)+"px";
22729         s.top = (t+a.t)+"px";
22730         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22731  
22732         if(s.width != sws || s.height != shs){
22733             s.width = sws;
22734             s.height = shs;
22735             if(!Roo.isIE){
22736                 var cn = d.childNodes;
22737                 var sww = Math.max(0, (sw-12))+"px";
22738                 cn[0].childNodes[1].style.width = sww;
22739                 cn[1].childNodes[1].style.width = sww;
22740                 cn[2].childNodes[1].style.width = sww;
22741                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22742             }
22743         }
22744     },
22745
22746     /**
22747      * Hides this shadow
22748      */
22749     hide : function(){
22750         if(this.el){
22751             this.el.dom.style.display = "none";
22752             Roo.Shadow.Pool.push(this.el);
22753             delete this.el;
22754         }
22755     },
22756
22757     /**
22758      * Adjust the z-index of this shadow
22759      * @param {Number} zindex The new z-index
22760      */
22761     setZIndex : function(z){
22762         this.zIndex = z;
22763         if(this.el){
22764             this.el.setStyle("z-index", z);
22765         }
22766     }
22767 };
22768
22769 // Private utility class that manages the internal Shadow cache
22770 Roo.Shadow.Pool = function(){
22771     var p = [];
22772     var markup = Roo.isIE ?
22773                  '<div class="x-ie-shadow"></div>' :
22774                  '<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>';
22775     return {
22776         pull : function(){
22777             var sh = p.shift();
22778             if(!sh){
22779                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22780                 sh.autoBoxAdjust = false;
22781             }
22782             return sh;
22783         },
22784
22785         push : function(sh){
22786             p.push(sh);
22787         }
22788     };
22789 }();/*
22790  * Based on:
22791  * Ext JS Library 1.1.1
22792  * Copyright(c) 2006-2007, Ext JS, LLC.
22793  *
22794  * Originally Released Under LGPL - original licence link has changed is not relivant.
22795  *
22796  * Fork - LGPL
22797  * <script type="text/javascript">
22798  */
22799
22800 /**
22801  * @class Roo.BoxComponent
22802  * @extends Roo.Component
22803  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22804  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22805  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22806  * layout containers.
22807  * @constructor
22808  * @param {Roo.Element/String/Object} config The configuration options.
22809  */
22810 Roo.BoxComponent = function(config){
22811     Roo.Component.call(this, config);
22812     this.addEvents({
22813         /**
22814          * @event resize
22815          * Fires after the component is resized.
22816              * @param {Roo.Component} this
22817              * @param {Number} adjWidth The box-adjusted width that was set
22818              * @param {Number} adjHeight The box-adjusted height that was set
22819              * @param {Number} rawWidth The width that was originally specified
22820              * @param {Number} rawHeight The height that was originally specified
22821              */
22822         resize : true,
22823         /**
22824          * @event move
22825          * Fires after the component is moved.
22826              * @param {Roo.Component} this
22827              * @param {Number} x The new x position
22828              * @param {Number} y The new y position
22829              */
22830         move : true
22831     });
22832 };
22833
22834 Roo.extend(Roo.BoxComponent, Roo.Component, {
22835     // private, set in afterRender to signify that the component has been rendered
22836     boxReady : false,
22837     // private, used to defer height settings to subclasses
22838     deferHeight: false,
22839     /** @cfg {Number} width
22840      * width (optional) size of component
22841      */
22842      /** @cfg {Number} height
22843      * height (optional) size of component
22844      */
22845      
22846     /**
22847      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22848      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22849      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22850      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22851      * @return {Roo.BoxComponent} this
22852      */
22853     setSize : function(w, h){
22854         // support for standard size objects
22855         if(typeof w == 'object'){
22856             h = w.height;
22857             w = w.width;
22858         }
22859         // not rendered
22860         if(!this.boxReady){
22861             this.width = w;
22862             this.height = h;
22863             return this;
22864         }
22865
22866         // prevent recalcs when not needed
22867         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22868             return this;
22869         }
22870         this.lastSize = {width: w, height: h};
22871
22872         var adj = this.adjustSize(w, h);
22873         var aw = adj.width, ah = adj.height;
22874         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22875             var rz = this.getResizeEl();
22876             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22877                 rz.setSize(aw, ah);
22878             }else if(!this.deferHeight && ah !== undefined){
22879                 rz.setHeight(ah);
22880             }else if(aw !== undefined){
22881                 rz.setWidth(aw);
22882             }
22883             this.onResize(aw, ah, w, h);
22884             this.fireEvent('resize', this, aw, ah, w, h);
22885         }
22886         return this;
22887     },
22888
22889     /**
22890      * Gets the current size of the component's underlying element.
22891      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22892      */
22893     getSize : function(){
22894         return this.el.getSize();
22895     },
22896
22897     /**
22898      * Gets the current XY position of the component's underlying element.
22899      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22900      * @return {Array} The XY position of the element (e.g., [100, 200])
22901      */
22902     getPosition : function(local){
22903         if(local === true){
22904             return [this.el.getLeft(true), this.el.getTop(true)];
22905         }
22906         return this.xy || this.el.getXY();
22907     },
22908
22909     /**
22910      * Gets the current box measurements of the component's underlying element.
22911      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22912      * @returns {Object} box An object in the format {x, y, width, height}
22913      */
22914     getBox : function(local){
22915         var s = this.el.getSize();
22916         if(local){
22917             s.x = this.el.getLeft(true);
22918             s.y = this.el.getTop(true);
22919         }else{
22920             var xy = this.xy || this.el.getXY();
22921             s.x = xy[0];
22922             s.y = xy[1];
22923         }
22924         return s;
22925     },
22926
22927     /**
22928      * Sets the current box measurements of the component's underlying element.
22929      * @param {Object} box An object in the format {x, y, width, height}
22930      * @returns {Roo.BoxComponent} this
22931      */
22932     updateBox : function(box){
22933         this.setSize(box.width, box.height);
22934         this.setPagePosition(box.x, box.y);
22935         return this;
22936     },
22937
22938     // protected
22939     getResizeEl : function(){
22940         return this.resizeEl || this.el;
22941     },
22942
22943     // protected
22944     getPositionEl : function(){
22945         return this.positionEl || this.el;
22946     },
22947
22948     /**
22949      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22950      * This method fires the move event.
22951      * @param {Number} left The new left
22952      * @param {Number} top The new top
22953      * @returns {Roo.BoxComponent} this
22954      */
22955     setPosition : function(x, y){
22956         this.x = x;
22957         this.y = y;
22958         if(!this.boxReady){
22959             return this;
22960         }
22961         var adj = this.adjustPosition(x, y);
22962         var ax = adj.x, ay = adj.y;
22963
22964         var el = this.getPositionEl();
22965         if(ax !== undefined || ay !== undefined){
22966             if(ax !== undefined && ay !== undefined){
22967                 el.setLeftTop(ax, ay);
22968             }else if(ax !== undefined){
22969                 el.setLeft(ax);
22970             }else if(ay !== undefined){
22971                 el.setTop(ay);
22972             }
22973             this.onPosition(ax, ay);
22974             this.fireEvent('move', this, ax, ay);
22975         }
22976         return this;
22977     },
22978
22979     /**
22980      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22981      * This method fires the move event.
22982      * @param {Number} x The new x position
22983      * @param {Number} y The new y position
22984      * @returns {Roo.BoxComponent} this
22985      */
22986     setPagePosition : function(x, y){
22987         this.pageX = x;
22988         this.pageY = y;
22989         if(!this.boxReady){
22990             return;
22991         }
22992         if(x === undefined || y === undefined){ // cannot translate undefined points
22993             return;
22994         }
22995         var p = this.el.translatePoints(x, y);
22996         this.setPosition(p.left, p.top);
22997         return this;
22998     },
22999
23000     // private
23001     onRender : function(ct, position){
23002         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23003         if(this.resizeEl){
23004             this.resizeEl = Roo.get(this.resizeEl);
23005         }
23006         if(this.positionEl){
23007             this.positionEl = Roo.get(this.positionEl);
23008         }
23009     },
23010
23011     // private
23012     afterRender : function(){
23013         Roo.BoxComponent.superclass.afterRender.call(this);
23014         this.boxReady = true;
23015         this.setSize(this.width, this.height);
23016         if(this.x || this.y){
23017             this.setPosition(this.x, this.y);
23018         }
23019         if(this.pageX || this.pageY){
23020             this.setPagePosition(this.pageX, this.pageY);
23021         }
23022     },
23023
23024     /**
23025      * Force the component's size to recalculate based on the underlying element's current height and width.
23026      * @returns {Roo.BoxComponent} this
23027      */
23028     syncSize : function(){
23029         delete this.lastSize;
23030         this.setSize(this.el.getWidth(), this.el.getHeight());
23031         return this;
23032     },
23033
23034     /**
23035      * Called after the component is resized, this method is empty by default but can be implemented by any
23036      * subclass that needs to perform custom logic after a resize occurs.
23037      * @param {Number} adjWidth The box-adjusted width that was set
23038      * @param {Number} adjHeight The box-adjusted height that was set
23039      * @param {Number} rawWidth The width that was originally specified
23040      * @param {Number} rawHeight The height that was originally specified
23041      */
23042     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23043
23044     },
23045
23046     /**
23047      * Called after the component is moved, this method is empty by default but can be implemented by any
23048      * subclass that needs to perform custom logic after a move occurs.
23049      * @param {Number} x The new x position
23050      * @param {Number} y The new y position
23051      */
23052     onPosition : function(x, y){
23053
23054     },
23055
23056     // private
23057     adjustSize : function(w, h){
23058         if(this.autoWidth){
23059             w = 'auto';
23060         }
23061         if(this.autoHeight){
23062             h = 'auto';
23063         }
23064         return {width : w, height: h};
23065     },
23066
23067     // private
23068     adjustPosition : function(x, y){
23069         return {x : x, y: y};
23070     }
23071 });/*
23072  * Based on:
23073  * Ext JS Library 1.1.1
23074  * Copyright(c) 2006-2007, Ext JS, LLC.
23075  *
23076  * Originally Released Under LGPL - original licence link has changed is not relivant.
23077  *
23078  * Fork - LGPL
23079  * <script type="text/javascript">
23080  */
23081
23082
23083 /**
23084  * @class Roo.SplitBar
23085  * @extends Roo.util.Observable
23086  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23087  * <br><br>
23088  * Usage:
23089  * <pre><code>
23090 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23091                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23092 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23093 split.minSize = 100;
23094 split.maxSize = 600;
23095 split.animate = true;
23096 split.on('moved', splitterMoved);
23097 </code></pre>
23098  * @constructor
23099  * Create a new SplitBar
23100  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23101  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23102  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23103  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23104                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23105                         position of the SplitBar).
23106  */
23107 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23108     
23109     /** @private */
23110     this.el = Roo.get(dragElement, true);
23111     this.el.dom.unselectable = "on";
23112     /** @private */
23113     this.resizingEl = Roo.get(resizingElement, true);
23114
23115     /**
23116      * @private
23117      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23118      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23119      * @type Number
23120      */
23121     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23122     
23123     /**
23124      * The minimum size of the resizing element. (Defaults to 0)
23125      * @type Number
23126      */
23127     this.minSize = 0;
23128     
23129     /**
23130      * The maximum size of the resizing element. (Defaults to 2000)
23131      * @type Number
23132      */
23133     this.maxSize = 2000;
23134     
23135     /**
23136      * Whether to animate the transition to the new size
23137      * @type Boolean
23138      */
23139     this.animate = false;
23140     
23141     /**
23142      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23143      * @type Boolean
23144      */
23145     this.useShim = false;
23146     
23147     /** @private */
23148     this.shim = null;
23149     
23150     if(!existingProxy){
23151         /** @private */
23152         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23153     }else{
23154         this.proxy = Roo.get(existingProxy).dom;
23155     }
23156     /** @private */
23157     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23158     
23159     /** @private */
23160     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23161     
23162     /** @private */
23163     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23164     
23165     /** @private */
23166     this.dragSpecs = {};
23167     
23168     /**
23169      * @private The adapter to use to positon and resize elements
23170      */
23171     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23172     this.adapter.init(this);
23173     
23174     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23175         /** @private */
23176         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23177         this.el.addClass("x-splitbar-h");
23178     }else{
23179         /** @private */
23180         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23181         this.el.addClass("x-splitbar-v");
23182     }
23183     
23184     this.addEvents({
23185         /**
23186          * @event resize
23187          * Fires when the splitter is moved (alias for {@link #event-moved})
23188          * @param {Roo.SplitBar} this
23189          * @param {Number} newSize the new width or height
23190          */
23191         "resize" : true,
23192         /**
23193          * @event moved
23194          * Fires when the splitter is moved
23195          * @param {Roo.SplitBar} this
23196          * @param {Number} newSize the new width or height
23197          */
23198         "moved" : true,
23199         /**
23200          * @event beforeresize
23201          * Fires before the splitter is dragged
23202          * @param {Roo.SplitBar} this
23203          */
23204         "beforeresize" : true,
23205
23206         "beforeapply" : true
23207     });
23208
23209     Roo.util.Observable.call(this);
23210 };
23211
23212 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23213     onStartProxyDrag : function(x, y){
23214         this.fireEvent("beforeresize", this);
23215         if(!this.overlay){
23216             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23217             o.unselectable();
23218             o.enableDisplayMode("block");
23219             // all splitbars share the same overlay
23220             Roo.SplitBar.prototype.overlay = o;
23221         }
23222         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23223         this.overlay.show();
23224         Roo.get(this.proxy).setDisplayed("block");
23225         var size = this.adapter.getElementSize(this);
23226         this.activeMinSize = this.getMinimumSize();;
23227         this.activeMaxSize = this.getMaximumSize();;
23228         var c1 = size - this.activeMinSize;
23229         var c2 = Math.max(this.activeMaxSize - size, 0);
23230         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23231             this.dd.resetConstraints();
23232             this.dd.setXConstraint(
23233                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23234                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23235             );
23236             this.dd.setYConstraint(0, 0);
23237         }else{
23238             this.dd.resetConstraints();
23239             this.dd.setXConstraint(0, 0);
23240             this.dd.setYConstraint(
23241                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23242                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23243             );
23244          }
23245         this.dragSpecs.startSize = size;
23246         this.dragSpecs.startPoint = [x, y];
23247         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23248     },
23249     
23250     /** 
23251      * @private Called after the drag operation by the DDProxy
23252      */
23253     onEndProxyDrag : function(e){
23254         Roo.get(this.proxy).setDisplayed(false);
23255         var endPoint = Roo.lib.Event.getXY(e);
23256         if(this.overlay){
23257             this.overlay.hide();
23258         }
23259         var newSize;
23260         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23261             newSize = this.dragSpecs.startSize + 
23262                 (this.placement == Roo.SplitBar.LEFT ?
23263                     endPoint[0] - this.dragSpecs.startPoint[0] :
23264                     this.dragSpecs.startPoint[0] - endPoint[0]
23265                 );
23266         }else{
23267             newSize = this.dragSpecs.startSize + 
23268                 (this.placement == Roo.SplitBar.TOP ?
23269                     endPoint[1] - this.dragSpecs.startPoint[1] :
23270                     this.dragSpecs.startPoint[1] - endPoint[1]
23271                 );
23272         }
23273         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23274         if(newSize != this.dragSpecs.startSize){
23275             if(this.fireEvent('beforeapply', this, newSize) !== false){
23276                 this.adapter.setElementSize(this, newSize);
23277                 this.fireEvent("moved", this, newSize);
23278                 this.fireEvent("resize", this, newSize);
23279             }
23280         }
23281     },
23282     
23283     /**
23284      * Get the adapter this SplitBar uses
23285      * @return The adapter object
23286      */
23287     getAdapter : function(){
23288         return this.adapter;
23289     },
23290     
23291     /**
23292      * Set the adapter this SplitBar uses
23293      * @param {Object} adapter A SplitBar adapter object
23294      */
23295     setAdapter : function(adapter){
23296         this.adapter = adapter;
23297         this.adapter.init(this);
23298     },
23299     
23300     /**
23301      * Gets the minimum size for the resizing element
23302      * @return {Number} The minimum size
23303      */
23304     getMinimumSize : function(){
23305         return this.minSize;
23306     },
23307     
23308     /**
23309      * Sets the minimum size for the resizing element
23310      * @param {Number} minSize The minimum size
23311      */
23312     setMinimumSize : function(minSize){
23313         this.minSize = minSize;
23314     },
23315     
23316     /**
23317      * Gets the maximum size for the resizing element
23318      * @return {Number} The maximum size
23319      */
23320     getMaximumSize : function(){
23321         return this.maxSize;
23322     },
23323     
23324     /**
23325      * Sets the maximum size for the resizing element
23326      * @param {Number} maxSize The maximum size
23327      */
23328     setMaximumSize : function(maxSize){
23329         this.maxSize = maxSize;
23330     },
23331     
23332     /**
23333      * Sets the initialize size for the resizing element
23334      * @param {Number} size The initial size
23335      */
23336     setCurrentSize : function(size){
23337         var oldAnimate = this.animate;
23338         this.animate = false;
23339         this.adapter.setElementSize(this, size);
23340         this.animate = oldAnimate;
23341     },
23342     
23343     /**
23344      * Destroy this splitbar. 
23345      * @param {Boolean} removeEl True to remove the element
23346      */
23347     destroy : function(removeEl){
23348         if(this.shim){
23349             this.shim.remove();
23350         }
23351         this.dd.unreg();
23352         this.proxy.parentNode.removeChild(this.proxy);
23353         if(removeEl){
23354             this.el.remove();
23355         }
23356     }
23357 });
23358
23359 /**
23360  * @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.
23361  */
23362 Roo.SplitBar.createProxy = function(dir){
23363     var proxy = new Roo.Element(document.createElement("div"));
23364     proxy.unselectable();
23365     var cls = 'x-splitbar-proxy';
23366     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23367     document.body.appendChild(proxy.dom);
23368     return proxy.dom;
23369 };
23370
23371 /** 
23372  * @class Roo.SplitBar.BasicLayoutAdapter
23373  * Default Adapter. It assumes the splitter and resizing element are not positioned
23374  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23375  */
23376 Roo.SplitBar.BasicLayoutAdapter = function(){
23377 };
23378
23379 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23380     // do nothing for now
23381     init : function(s){
23382     
23383     },
23384     /**
23385      * Called before drag operations to get the current size of the resizing element. 
23386      * @param {Roo.SplitBar} s The SplitBar using this adapter
23387      */
23388      getElementSize : function(s){
23389         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23390             return s.resizingEl.getWidth();
23391         }else{
23392             return s.resizingEl.getHeight();
23393         }
23394     },
23395     
23396     /**
23397      * Called after drag operations to set the size of the resizing element.
23398      * @param {Roo.SplitBar} s The SplitBar using this adapter
23399      * @param {Number} newSize The new size to set
23400      * @param {Function} onComplete A function to be invoked when resizing is complete
23401      */
23402     setElementSize : function(s, newSize, onComplete){
23403         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23404             if(!s.animate){
23405                 s.resizingEl.setWidth(newSize);
23406                 if(onComplete){
23407                     onComplete(s, newSize);
23408                 }
23409             }else{
23410                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23411             }
23412         }else{
23413             
23414             if(!s.animate){
23415                 s.resizingEl.setHeight(newSize);
23416                 if(onComplete){
23417                     onComplete(s, newSize);
23418                 }
23419             }else{
23420                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23421             }
23422         }
23423     }
23424 };
23425
23426 /** 
23427  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23428  * @extends Roo.SplitBar.BasicLayoutAdapter
23429  * Adapter that  moves the splitter element to align with the resized sizing element. 
23430  * Used with an absolute positioned SplitBar.
23431  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23432  * document.body, make sure you assign an id to the body element.
23433  */
23434 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23435     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23436     this.container = Roo.get(container);
23437 };
23438
23439 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23440     init : function(s){
23441         this.basic.init(s);
23442     },
23443     
23444     getElementSize : function(s){
23445         return this.basic.getElementSize(s);
23446     },
23447     
23448     setElementSize : function(s, newSize, onComplete){
23449         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23450     },
23451     
23452     moveSplitter : function(s){
23453         var yes = Roo.SplitBar;
23454         switch(s.placement){
23455             case yes.LEFT:
23456                 s.el.setX(s.resizingEl.getRight());
23457                 break;
23458             case yes.RIGHT:
23459                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23460                 break;
23461             case yes.TOP:
23462                 s.el.setY(s.resizingEl.getBottom());
23463                 break;
23464             case yes.BOTTOM:
23465                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23466                 break;
23467         }
23468     }
23469 };
23470
23471 /**
23472  * Orientation constant - Create a vertical SplitBar
23473  * @static
23474  * @type Number
23475  */
23476 Roo.SplitBar.VERTICAL = 1;
23477
23478 /**
23479  * Orientation constant - Create a horizontal SplitBar
23480  * @static
23481  * @type Number
23482  */
23483 Roo.SplitBar.HORIZONTAL = 2;
23484
23485 /**
23486  * Placement constant - The resizing element is to the left of the splitter element
23487  * @static
23488  * @type Number
23489  */
23490 Roo.SplitBar.LEFT = 1;
23491
23492 /**
23493  * Placement constant - The resizing element is to the right of the splitter element
23494  * @static
23495  * @type Number
23496  */
23497 Roo.SplitBar.RIGHT = 2;
23498
23499 /**
23500  * Placement constant - The resizing element is positioned above the splitter element
23501  * @static
23502  * @type Number
23503  */
23504 Roo.SplitBar.TOP = 3;
23505
23506 /**
23507  * Placement constant - The resizing element is positioned under splitter element
23508  * @static
23509  * @type Number
23510  */
23511 Roo.SplitBar.BOTTOM = 4;
23512 /*
23513  * Based on:
23514  * Ext JS Library 1.1.1
23515  * Copyright(c) 2006-2007, Ext JS, LLC.
23516  *
23517  * Originally Released Under LGPL - original licence link has changed is not relivant.
23518  *
23519  * Fork - LGPL
23520  * <script type="text/javascript">
23521  */
23522
23523 /**
23524  * @class Roo.View
23525  * @extends Roo.util.Observable
23526  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23527  * This class also supports single and multi selection modes. <br>
23528  * Create a data model bound view:
23529  <pre><code>
23530  var store = new Roo.data.Store(...);
23531
23532  var view = new Roo.View({
23533     el : "my-element",
23534     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23535  
23536     singleSelect: true,
23537     selectedClass: "ydataview-selected",
23538     store: store
23539  });
23540
23541  // listen for node click?
23542  view.on("click", function(vw, index, node, e){
23543  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23544  });
23545
23546  // load XML data
23547  dataModel.load("foobar.xml");
23548  </code></pre>
23549  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23550  * <br><br>
23551  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23552  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23553  * 
23554  * Note: old style constructor is still suported (container, template, config)
23555  * 
23556  * @constructor
23557  * Create a new View
23558  * @param {Object} config The config object
23559  * 
23560  */
23561 Roo.View = function(config, depreciated_tpl, depreciated_config){
23562     
23563     if (typeof(depreciated_tpl) == 'undefined') {
23564         // new way.. - universal constructor.
23565         Roo.apply(this, config);
23566         this.el  = Roo.get(this.el);
23567     } else {
23568         // old format..
23569         this.el  = Roo.get(config);
23570         this.tpl = depreciated_tpl;
23571         Roo.apply(this, depreciated_config);
23572     }
23573      
23574     
23575     if(typeof(this.tpl) == "string"){
23576         this.tpl = new Roo.Template(this.tpl);
23577     } else {
23578         // support xtype ctors..
23579         this.tpl = new Roo.factory(this.tpl, Roo);
23580     }
23581     
23582     
23583     this.tpl.compile();
23584    
23585
23586      
23587     /** @private */
23588     this.addEvents({
23589     /**
23590      * @event beforeclick
23591      * Fires before a click is processed. Returns false to cancel the default action.
23592      * @param {Roo.View} this
23593      * @param {Number} index The index of the target node
23594      * @param {HTMLElement} node The target node
23595      * @param {Roo.EventObject} e The raw event object
23596      */
23597         "beforeclick" : true,
23598     /**
23599      * @event click
23600      * Fires when a template node is clicked.
23601      * @param {Roo.View} this
23602      * @param {Number} index The index of the target node
23603      * @param {HTMLElement} node The target node
23604      * @param {Roo.EventObject} e The raw event object
23605      */
23606         "click" : true,
23607     /**
23608      * @event dblclick
23609      * Fires when a template node is double clicked.
23610      * @param {Roo.View} this
23611      * @param {Number} index The index of the target node
23612      * @param {HTMLElement} node The target node
23613      * @param {Roo.EventObject} e The raw event object
23614      */
23615         "dblclick" : true,
23616     /**
23617      * @event contextmenu
23618      * Fires when a template node is right clicked.
23619      * @param {Roo.View} this
23620      * @param {Number} index The index of the target node
23621      * @param {HTMLElement} node The target node
23622      * @param {Roo.EventObject} e The raw event object
23623      */
23624         "contextmenu" : true,
23625     /**
23626      * @event selectionchange
23627      * Fires when the selected nodes change.
23628      * @param {Roo.View} this
23629      * @param {Array} selections Array of the selected nodes
23630      */
23631         "selectionchange" : true,
23632
23633     /**
23634      * @event beforeselect
23635      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23636      * @param {Roo.View} this
23637      * @param {HTMLElement} node The node to be selected
23638      * @param {Array} selections Array of currently selected nodes
23639      */
23640         "beforeselect" : true
23641     });
23642
23643     this.el.on({
23644         "click": this.onClick,
23645         "dblclick": this.onDblClick,
23646         "contextmenu": this.onContextMenu,
23647         scope:this
23648     });
23649
23650     this.selections = [];
23651     this.nodes = [];
23652     this.cmp = new Roo.CompositeElementLite([]);
23653     if(this.store){
23654         this.store = Roo.factory(this.store, Roo.data);
23655         this.setStore(this.store, true);
23656     }
23657     Roo.View.superclass.constructor.call(this);
23658 };
23659
23660 Roo.extend(Roo.View, Roo.util.Observable, {
23661     
23662      /**
23663      * @cfg {Roo.data.Store} store Data store to load data from.
23664      */
23665     store : false,
23666     
23667     /**
23668      * @cfg {String|Roo.Element} el The container element.
23669      */
23670     el : '',
23671     
23672     /**
23673      * @cfg {String|Roo.Template} tpl The template used by this View 
23674      */
23675     tpl : false,
23676     
23677     /**
23678      * @cfg {String} selectedClass The css class to add to selected nodes
23679      */
23680     selectedClass : "x-view-selected",
23681      /**
23682      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23683      */
23684     emptyText : "",
23685     /**
23686      * @cfg {Boolean} multiSelect Allow multiple selection
23687      */
23688     
23689     multiSelect : false,
23690     /**
23691      * @cfg {Boolean} singleSelect Allow single selection
23692      */
23693     singleSelect:  false,
23694     
23695     /**
23696      * Returns the element this view is bound to.
23697      * @return {Roo.Element}
23698      */
23699     getEl : function(){
23700         return this.el;
23701     },
23702
23703     /**
23704      * Refreshes the view.
23705      */
23706     refresh : function(){
23707         var t = this.tpl;
23708         this.clearSelections();
23709         this.el.update("");
23710         var html = [];
23711         var records = this.store.getRange();
23712         if(records.length < 1){
23713             this.el.update(this.emptyText);
23714             return;
23715         }
23716         for(var i = 0, len = records.length; i < len; i++){
23717             var data = this.prepareData(records[i].data, i, records[i]);
23718             html[html.length] = t.apply(data);
23719         }
23720         this.el.update(html.join(""));
23721         this.nodes = this.el.dom.childNodes;
23722         this.updateIndexes(0);
23723     },
23724
23725     /**
23726      * Function to override to reformat the data that is sent to
23727      * the template for each node.
23728      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23729      * a JSON object for an UpdateManager bound view).
23730      */
23731     prepareData : function(data){
23732         return data;
23733     },
23734
23735     onUpdate : function(ds, record){
23736         this.clearSelections();
23737         var index = this.store.indexOf(record);
23738         var n = this.nodes[index];
23739         this.tpl.insertBefore(n, this.prepareData(record.data));
23740         n.parentNode.removeChild(n);
23741         this.updateIndexes(index, index);
23742     },
23743
23744     onAdd : function(ds, records, index){
23745         this.clearSelections();
23746         if(this.nodes.length == 0){
23747             this.refresh();
23748             return;
23749         }
23750         var n = this.nodes[index];
23751         for(var i = 0, len = records.length; i < len; i++){
23752             var d = this.prepareData(records[i].data);
23753             if(n){
23754                 this.tpl.insertBefore(n, d);
23755             }else{
23756                 this.tpl.append(this.el, d);
23757             }
23758         }
23759         this.updateIndexes(index);
23760     },
23761
23762     onRemove : function(ds, record, index){
23763         this.clearSelections();
23764         this.el.dom.removeChild(this.nodes[index]);
23765         this.updateIndexes(index);
23766     },
23767
23768     /**
23769      * Refresh an individual node.
23770      * @param {Number} index
23771      */
23772     refreshNode : function(index){
23773         this.onUpdate(this.store, this.store.getAt(index));
23774     },
23775
23776     updateIndexes : function(startIndex, endIndex){
23777         var ns = this.nodes;
23778         startIndex = startIndex || 0;
23779         endIndex = endIndex || ns.length - 1;
23780         for(var i = startIndex; i <= endIndex; i++){
23781             ns[i].nodeIndex = i;
23782         }
23783     },
23784
23785     /**
23786      * Changes the data store this view uses and refresh the view.
23787      * @param {Store} store
23788      */
23789     setStore : function(store, initial){
23790         if(!initial && this.store){
23791             this.store.un("datachanged", this.refresh);
23792             this.store.un("add", this.onAdd);
23793             this.store.un("remove", this.onRemove);
23794             this.store.un("update", this.onUpdate);
23795             this.store.un("clear", this.refresh);
23796         }
23797         if(store){
23798           
23799             store.on("datachanged", this.refresh, this);
23800             store.on("add", this.onAdd, this);
23801             store.on("remove", this.onRemove, this);
23802             store.on("update", this.onUpdate, this);
23803             store.on("clear", this.refresh, this);
23804         }
23805         
23806         if(store){
23807             this.refresh();
23808         }
23809     },
23810
23811     /**
23812      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23813      * @param {HTMLElement} node
23814      * @return {HTMLElement} The template node
23815      */
23816     findItemFromChild : function(node){
23817         var el = this.el.dom;
23818         if(!node || node.parentNode == el){
23819                     return node;
23820             }
23821             var p = node.parentNode;
23822             while(p && p != el){
23823             if(p.parentNode == el){
23824                 return p;
23825             }
23826             p = p.parentNode;
23827         }
23828             return null;
23829     },
23830
23831     /** @ignore */
23832     onClick : function(e){
23833         var item = this.findItemFromChild(e.getTarget());
23834         if(item){
23835             var index = this.indexOf(item);
23836             if(this.onItemClick(item, index, e) !== false){
23837                 this.fireEvent("click", this, index, item, e);
23838             }
23839         }else{
23840             this.clearSelections();
23841         }
23842     },
23843
23844     /** @ignore */
23845     onContextMenu : function(e){
23846         var item = this.findItemFromChild(e.getTarget());
23847         if(item){
23848             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23849         }
23850     },
23851
23852     /** @ignore */
23853     onDblClick : function(e){
23854         var item = this.findItemFromChild(e.getTarget());
23855         if(item){
23856             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23857         }
23858     },
23859
23860     onItemClick : function(item, index, e){
23861         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23862             return false;
23863         }
23864         if(this.multiSelect || this.singleSelect){
23865             if(this.multiSelect && e.shiftKey && this.lastSelection){
23866                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23867             }else{
23868                 this.select(item, this.multiSelect && e.ctrlKey);
23869                 this.lastSelection = item;
23870             }
23871             e.preventDefault();
23872         }
23873         return true;
23874     },
23875
23876     /**
23877      * Get the number of selected nodes.
23878      * @return {Number}
23879      */
23880     getSelectionCount : function(){
23881         return this.selections.length;
23882     },
23883
23884     /**
23885      * Get the currently selected nodes.
23886      * @return {Array} An array of HTMLElements
23887      */
23888     getSelectedNodes : function(){
23889         return this.selections;
23890     },
23891
23892     /**
23893      * Get the indexes of the selected nodes.
23894      * @return {Array}
23895      */
23896     getSelectedIndexes : function(){
23897         var indexes = [], s = this.selections;
23898         for(var i = 0, len = s.length; i < len; i++){
23899             indexes.push(s[i].nodeIndex);
23900         }
23901         return indexes;
23902     },
23903
23904     /**
23905      * Clear all selections
23906      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23907      */
23908     clearSelections : function(suppressEvent){
23909         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23910             this.cmp.elements = this.selections;
23911             this.cmp.removeClass(this.selectedClass);
23912             this.selections = [];
23913             if(!suppressEvent){
23914                 this.fireEvent("selectionchange", this, this.selections);
23915             }
23916         }
23917     },
23918
23919     /**
23920      * Returns true if the passed node is selected
23921      * @param {HTMLElement/Number} node The node or node index
23922      * @return {Boolean}
23923      */
23924     isSelected : function(node){
23925         var s = this.selections;
23926         if(s.length < 1){
23927             return false;
23928         }
23929         node = this.getNode(node);
23930         return s.indexOf(node) !== -1;
23931     },
23932
23933     /**
23934      * Selects nodes.
23935      * @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
23936      * @param {Boolean} keepExisting (optional) true to keep existing selections
23937      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23938      */
23939     select : function(nodeInfo, keepExisting, suppressEvent){
23940         if(nodeInfo instanceof Array){
23941             if(!keepExisting){
23942                 this.clearSelections(true);
23943             }
23944             for(var i = 0, len = nodeInfo.length; i < len; i++){
23945                 this.select(nodeInfo[i], true, true);
23946             }
23947         } else{
23948             var node = this.getNode(nodeInfo);
23949             if(node && !this.isSelected(node)){
23950                 if(!keepExisting){
23951                     this.clearSelections(true);
23952                 }
23953                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23954                     Roo.fly(node).addClass(this.selectedClass);
23955                     this.selections.push(node);
23956                     if(!suppressEvent){
23957                         this.fireEvent("selectionchange", this, this.selections);
23958                     }
23959                 }
23960             }
23961         }
23962     },
23963
23964     /**
23965      * Gets a template node.
23966      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23967      * @return {HTMLElement} The node or null if it wasn't found
23968      */
23969     getNode : function(nodeInfo){
23970         if(typeof nodeInfo == "string"){
23971             return document.getElementById(nodeInfo);
23972         }else if(typeof nodeInfo == "number"){
23973             return this.nodes[nodeInfo];
23974         }
23975         return nodeInfo;
23976     },
23977
23978     /**
23979      * Gets a range template nodes.
23980      * @param {Number} startIndex
23981      * @param {Number} endIndex
23982      * @return {Array} An array of nodes
23983      */
23984     getNodes : function(start, end){
23985         var ns = this.nodes;
23986         start = start || 0;
23987         end = typeof end == "undefined" ? ns.length - 1 : end;
23988         var nodes = [];
23989         if(start <= end){
23990             for(var i = start; i <= end; i++){
23991                 nodes.push(ns[i]);
23992             }
23993         } else{
23994             for(var i = start; i >= end; i--){
23995                 nodes.push(ns[i]);
23996             }
23997         }
23998         return nodes;
23999     },
24000
24001     /**
24002      * Finds the index of the passed node
24003      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24004      * @return {Number} The index of the node or -1
24005      */
24006     indexOf : function(node){
24007         node = this.getNode(node);
24008         if(typeof node.nodeIndex == "number"){
24009             return node.nodeIndex;
24010         }
24011         var ns = this.nodes;
24012         for(var i = 0, len = ns.length; i < len; i++){
24013             if(ns[i] == node){
24014                 return i;
24015             }
24016         }
24017         return -1;
24018     }
24019 });
24020 /*
24021  * Based on:
24022  * Ext JS Library 1.1.1
24023  * Copyright(c) 2006-2007, Ext JS, LLC.
24024  *
24025  * Originally Released Under LGPL - original licence link has changed is not relivant.
24026  *
24027  * Fork - LGPL
24028  * <script type="text/javascript">
24029  */
24030
24031 /**
24032  * @class Roo.JsonView
24033  * @extends Roo.View
24034  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24035 <pre><code>
24036 var view = new Roo.JsonView({
24037     container: "my-element",
24038     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24039     multiSelect: true, 
24040     jsonRoot: "data" 
24041 });
24042
24043 // listen for node click?
24044 view.on("click", function(vw, index, node, e){
24045     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24046 });
24047
24048 // direct load of JSON data
24049 view.load("foobar.php");
24050
24051 // Example from my blog list
24052 var tpl = new Roo.Template(
24053     '&lt;div class="entry"&gt;' +
24054     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24055     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24056     "&lt;/div&gt;&lt;hr /&gt;"
24057 );
24058
24059 var moreView = new Roo.JsonView({
24060     container :  "entry-list", 
24061     template : tpl,
24062     jsonRoot: "posts"
24063 });
24064 moreView.on("beforerender", this.sortEntries, this);
24065 moreView.load({
24066     url: "/blog/get-posts.php",
24067     params: "allposts=true",
24068     text: "Loading Blog Entries..."
24069 });
24070 </code></pre>
24071
24072 * Note: old code is supported with arguments : (container, template, config)
24073
24074
24075  * @constructor
24076  * Create a new JsonView
24077  * 
24078  * @param {Object} config The config object
24079  * 
24080  */
24081 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24082     
24083     
24084     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24085
24086     var um = this.el.getUpdateManager();
24087     um.setRenderer(this);
24088     um.on("update", this.onLoad, this);
24089     um.on("failure", this.onLoadException, this);
24090
24091     /**
24092      * @event beforerender
24093      * Fires before rendering of the downloaded JSON data.
24094      * @param {Roo.JsonView} this
24095      * @param {Object} data The JSON data loaded
24096      */
24097     /**
24098      * @event load
24099      * Fires when data is loaded.
24100      * @param {Roo.JsonView} this
24101      * @param {Object} data The JSON data loaded
24102      * @param {Object} response The raw Connect response object
24103      */
24104     /**
24105      * @event loadexception
24106      * Fires when loading fails.
24107      * @param {Roo.JsonView} this
24108      * @param {Object} response The raw Connect response object
24109      */
24110     this.addEvents({
24111         'beforerender' : true,
24112         'load' : true,
24113         'loadexception' : true
24114     });
24115 };
24116 Roo.extend(Roo.JsonView, Roo.View, {
24117     /**
24118      * @type {String} The root property in the loaded JSON object that contains the data
24119      */
24120     jsonRoot : "",
24121
24122     /**
24123      * Refreshes the view.
24124      */
24125     refresh : function(){
24126         this.clearSelections();
24127         this.el.update("");
24128         var html = [];
24129         var o = this.jsonData;
24130         if(o && o.length > 0){
24131             for(var i = 0, len = o.length; i < len; i++){
24132                 var data = this.prepareData(o[i], i, o);
24133                 html[html.length] = this.tpl.apply(data);
24134             }
24135         }else{
24136             html.push(this.emptyText);
24137         }
24138         this.el.update(html.join(""));
24139         this.nodes = this.el.dom.childNodes;
24140         this.updateIndexes(0);
24141     },
24142
24143     /**
24144      * 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.
24145      * @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:
24146      <pre><code>
24147      view.load({
24148          url: "your-url.php",
24149          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24150          callback: yourFunction,
24151          scope: yourObject, //(optional scope)
24152          discardUrl: false,
24153          nocache: false,
24154          text: "Loading...",
24155          timeout: 30,
24156          scripts: false
24157      });
24158      </code></pre>
24159      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24160      * 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.
24161      * @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}
24162      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24163      * @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.
24164      */
24165     load : function(){
24166         var um = this.el.getUpdateManager();
24167         um.update.apply(um, arguments);
24168     },
24169
24170     render : function(el, response){
24171         this.clearSelections();
24172         this.el.update("");
24173         var o;
24174         try{
24175             o = Roo.util.JSON.decode(response.responseText);
24176             if(this.jsonRoot){
24177                 
24178                 o = o[this.jsonRoot];
24179             }
24180         } catch(e){
24181         }
24182         /**
24183          * The current JSON data or null
24184          */
24185         this.jsonData = o;
24186         this.beforeRender();
24187         this.refresh();
24188     },
24189
24190 /**
24191  * Get the number of records in the current JSON dataset
24192  * @return {Number}
24193  */
24194     getCount : function(){
24195         return this.jsonData ? this.jsonData.length : 0;
24196     },
24197
24198 /**
24199  * Returns the JSON object for the specified node(s)
24200  * @param {HTMLElement/Array} node The node or an array of nodes
24201  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24202  * you get the JSON object for the node
24203  */
24204     getNodeData : function(node){
24205         if(node instanceof Array){
24206             var data = [];
24207             for(var i = 0, len = node.length; i < len; i++){
24208                 data.push(this.getNodeData(node[i]));
24209             }
24210             return data;
24211         }
24212         return this.jsonData[this.indexOf(node)] || null;
24213     },
24214
24215     beforeRender : function(){
24216         this.snapshot = this.jsonData;
24217         if(this.sortInfo){
24218             this.sort.apply(this, this.sortInfo);
24219         }
24220         this.fireEvent("beforerender", this, this.jsonData);
24221     },
24222
24223     onLoad : function(el, o){
24224         this.fireEvent("load", this, this.jsonData, o);
24225     },
24226
24227     onLoadException : function(el, o){
24228         this.fireEvent("loadexception", this, o);
24229     },
24230
24231 /**
24232  * Filter the data by a specific property.
24233  * @param {String} property A property on your JSON objects
24234  * @param {String/RegExp} value Either string that the property values
24235  * should start with, or a RegExp to test against the property
24236  */
24237     filter : function(property, value){
24238         if(this.jsonData){
24239             var data = [];
24240             var ss = this.snapshot;
24241             if(typeof value == "string"){
24242                 var vlen = value.length;
24243                 if(vlen == 0){
24244                     this.clearFilter();
24245                     return;
24246                 }
24247                 value = value.toLowerCase();
24248                 for(var i = 0, len = ss.length; i < len; i++){
24249                     var o = ss[i];
24250                     if(o[property].substr(0, vlen).toLowerCase() == value){
24251                         data.push(o);
24252                     }
24253                 }
24254             } else if(value.exec){ // regex?
24255                 for(var i = 0, len = ss.length; i < len; i++){
24256                     var o = ss[i];
24257                     if(value.test(o[property])){
24258                         data.push(o);
24259                     }
24260                 }
24261             } else{
24262                 return;
24263             }
24264             this.jsonData = data;
24265             this.refresh();
24266         }
24267     },
24268
24269 /**
24270  * Filter by a function. The passed function will be called with each
24271  * object in the current dataset. If the function returns true the value is kept,
24272  * otherwise it is filtered.
24273  * @param {Function} fn
24274  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24275  */
24276     filterBy : function(fn, scope){
24277         if(this.jsonData){
24278             var data = [];
24279             var ss = this.snapshot;
24280             for(var i = 0, len = ss.length; i < len; i++){
24281                 var o = ss[i];
24282                 if(fn.call(scope || this, o)){
24283                     data.push(o);
24284                 }
24285             }
24286             this.jsonData = data;
24287             this.refresh();
24288         }
24289     },
24290
24291 /**
24292  * Clears the current filter.
24293  */
24294     clearFilter : function(){
24295         if(this.snapshot && this.jsonData != this.snapshot){
24296             this.jsonData = this.snapshot;
24297             this.refresh();
24298         }
24299     },
24300
24301
24302 /**
24303  * Sorts the data for this view and refreshes it.
24304  * @param {String} property A property on your JSON objects to sort on
24305  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24306  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24307  */
24308     sort : function(property, dir, sortType){
24309         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24310         if(this.jsonData){
24311             var p = property;
24312             var dsc = dir && dir.toLowerCase() == "desc";
24313             var f = function(o1, o2){
24314                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24315                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24316                 ;
24317                 if(v1 < v2){
24318                     return dsc ? +1 : -1;
24319                 } else if(v1 > v2){
24320                     return dsc ? -1 : +1;
24321                 } else{
24322                     return 0;
24323                 }
24324             };
24325             this.jsonData.sort(f);
24326             this.refresh();
24327             if(this.jsonData != this.snapshot){
24328                 this.snapshot.sort(f);
24329             }
24330         }
24331     }
24332 });/*
24333  * Based on:
24334  * Ext JS Library 1.1.1
24335  * Copyright(c) 2006-2007, Ext JS, LLC.
24336  *
24337  * Originally Released Under LGPL - original licence link has changed is not relivant.
24338  *
24339  * Fork - LGPL
24340  * <script type="text/javascript">
24341  */
24342  
24343
24344 /**
24345  * @class Roo.ColorPalette
24346  * @extends Roo.Component
24347  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24348  * Here's an example of typical usage:
24349  * <pre><code>
24350 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24351 cp.render('my-div');
24352
24353 cp.on('select', function(palette, selColor){
24354     // do something with selColor
24355 });
24356 </code></pre>
24357  * @constructor
24358  * Create a new ColorPalette
24359  * @param {Object} config The config object
24360  */
24361 Roo.ColorPalette = function(config){
24362     Roo.ColorPalette.superclass.constructor.call(this, config);
24363     this.addEvents({
24364         /**
24365              * @event select
24366              * Fires when a color is selected
24367              * @param {ColorPalette} this
24368              * @param {String} color The 6-digit color hex code (without the # symbol)
24369              */
24370         select: true
24371     });
24372
24373     if(this.handler){
24374         this.on("select", this.handler, this.scope, true);
24375     }
24376 };
24377 Roo.extend(Roo.ColorPalette, Roo.Component, {
24378     /**
24379      * @cfg {String} itemCls
24380      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24381      */
24382     itemCls : "x-color-palette",
24383     /**
24384      * @cfg {String} value
24385      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24386      * the hex codes are case-sensitive.
24387      */
24388     value : null,
24389     clickEvent:'click',
24390     // private
24391     ctype: "Roo.ColorPalette",
24392
24393     /**
24394      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24395      */
24396     allowReselect : false,
24397
24398     /**
24399      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24400      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24401      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24402      * of colors with the width setting until the box is symmetrical.</p>
24403      * <p>You can override individual colors if needed:</p>
24404      * <pre><code>
24405 var cp = new Roo.ColorPalette();
24406 cp.colors[0] = "FF0000";  // change the first box to red
24407 </code></pre>
24408
24409 Or you can provide a custom array of your own for complete control:
24410 <pre><code>
24411 var cp = new Roo.ColorPalette();
24412 cp.colors = ["000000", "993300", "333300"];
24413 </code></pre>
24414      * @type Array
24415      */
24416     colors : [
24417         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24418         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24419         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24420         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24421         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24422     ],
24423
24424     // private
24425     onRender : function(container, position){
24426         var t = new Roo.MasterTemplate(
24427             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24428         );
24429         var c = this.colors;
24430         for(var i = 0, len = c.length; i < len; i++){
24431             t.add([c[i]]);
24432         }
24433         var el = document.createElement("div");
24434         el.className = this.itemCls;
24435         t.overwrite(el);
24436         container.dom.insertBefore(el, position);
24437         this.el = Roo.get(el);
24438         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24439         if(this.clickEvent != 'click'){
24440             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24441         }
24442     },
24443
24444     // private
24445     afterRender : function(){
24446         Roo.ColorPalette.superclass.afterRender.call(this);
24447         if(this.value){
24448             var s = this.value;
24449             this.value = null;
24450             this.select(s);
24451         }
24452     },
24453
24454     // private
24455     handleClick : function(e, t){
24456         e.preventDefault();
24457         if(!this.disabled){
24458             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24459             this.select(c.toUpperCase());
24460         }
24461     },
24462
24463     /**
24464      * Selects the specified color in the palette (fires the select event)
24465      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24466      */
24467     select : function(color){
24468         color = color.replace("#", "");
24469         if(color != this.value || this.allowReselect){
24470             var el = this.el;
24471             if(this.value){
24472                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24473             }
24474             el.child("a.color-"+color).addClass("x-color-palette-sel");
24475             this.value = color;
24476             this.fireEvent("select", this, color);
24477         }
24478     }
24479 });/*
24480  * Based on:
24481  * Ext JS Library 1.1.1
24482  * Copyright(c) 2006-2007, Ext JS, LLC.
24483  *
24484  * Originally Released Under LGPL - original licence link has changed is not relivant.
24485  *
24486  * Fork - LGPL
24487  * <script type="text/javascript">
24488  */
24489  
24490 /**
24491  * @class Roo.DatePicker
24492  * @extends Roo.Component
24493  * Simple date picker class.
24494  * @constructor
24495  * Create a new DatePicker
24496  * @param {Object} config The config object
24497  */
24498 Roo.DatePicker = function(config){
24499     Roo.DatePicker.superclass.constructor.call(this, config);
24500
24501     this.value = config && config.value ?
24502                  config.value.clearTime() : new Date().clearTime();
24503
24504     this.addEvents({
24505         /**
24506              * @event select
24507              * Fires when a date is selected
24508              * @param {DatePicker} this
24509              * @param {Date} date The selected date
24510              */
24511         select: true
24512     });
24513
24514     if(this.handler){
24515         this.on("select", this.handler,  this.scope || this);
24516     }
24517     // build the disabledDatesRE
24518     if(!this.disabledDatesRE && this.disabledDates){
24519         var dd = this.disabledDates;
24520         var re = "(?:";
24521         for(var i = 0; i < dd.length; i++){
24522             re += dd[i];
24523             if(i != dd.length-1) re += "|";
24524         }
24525         this.disabledDatesRE = new RegExp(re + ")");
24526     }
24527 };
24528
24529 Roo.extend(Roo.DatePicker, Roo.Component, {
24530     /**
24531      * @cfg {String} todayText
24532      * The text to display on the button that selects the current date (defaults to "Today")
24533      */
24534     todayText : "Today",
24535     /**
24536      * @cfg {String} okText
24537      * The text to display on the ok button
24538      */
24539     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24540     /**
24541      * @cfg {String} cancelText
24542      * The text to display on the cancel button
24543      */
24544     cancelText : "Cancel",
24545     /**
24546      * @cfg {String} todayTip
24547      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24548      */
24549     todayTip : "{0} (Spacebar)",
24550     /**
24551      * @cfg {Date} minDate
24552      * Minimum allowable date (JavaScript date object, defaults to null)
24553      */
24554     minDate : null,
24555     /**
24556      * @cfg {Date} maxDate
24557      * Maximum allowable date (JavaScript date object, defaults to null)
24558      */
24559     maxDate : null,
24560     /**
24561      * @cfg {String} minText
24562      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24563      */
24564     minText : "This date is before the minimum date",
24565     /**
24566      * @cfg {String} maxText
24567      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24568      */
24569     maxText : "This date is after the maximum date",
24570     /**
24571      * @cfg {String} format
24572      * The default date format string which can be overriden for localization support.  The format must be
24573      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24574      */
24575     format : "m/d/y",
24576     /**
24577      * @cfg {Array} disabledDays
24578      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24579      */
24580     disabledDays : null,
24581     /**
24582      * @cfg {String} disabledDaysText
24583      * The tooltip to display when the date falls on a disabled day (defaults to "")
24584      */
24585     disabledDaysText : "",
24586     /**
24587      * @cfg {RegExp} disabledDatesRE
24588      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24589      */
24590     disabledDatesRE : null,
24591     /**
24592      * @cfg {String} disabledDatesText
24593      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24594      */
24595     disabledDatesText : "",
24596     /**
24597      * @cfg {Boolean} constrainToViewport
24598      * True to constrain the date picker to the viewport (defaults to true)
24599      */
24600     constrainToViewport : true,
24601     /**
24602      * @cfg {Array} monthNames
24603      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24604      */
24605     monthNames : Date.monthNames,
24606     /**
24607      * @cfg {Array} dayNames
24608      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24609      */
24610     dayNames : Date.dayNames,
24611     /**
24612      * @cfg {String} nextText
24613      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24614      */
24615     nextText: 'Next Month (Control+Right)',
24616     /**
24617      * @cfg {String} prevText
24618      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24619      */
24620     prevText: 'Previous Month (Control+Left)',
24621     /**
24622      * @cfg {String} monthYearText
24623      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24624      */
24625     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24626     /**
24627      * @cfg {Number} startDay
24628      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24629      */
24630     startDay : 0,
24631     /**
24632      * @cfg {Bool} showClear
24633      * Show a clear button (usefull for date form elements that can be blank.)
24634      */
24635     
24636     showClear: false,
24637     
24638     /**
24639      * Sets the value of the date field
24640      * @param {Date} value The date to set
24641      */
24642     setValue : function(value){
24643         var old = this.value;
24644         this.value = value.clearTime(true);
24645         if(this.el){
24646             this.update(this.value);
24647         }
24648     },
24649
24650     /**
24651      * Gets the current selected value of the date field
24652      * @return {Date} The selected date
24653      */
24654     getValue : function(){
24655         return this.value;
24656     },
24657
24658     // private
24659     focus : function(){
24660         if(this.el){
24661             this.update(this.activeDate);
24662         }
24663     },
24664
24665     // private
24666     onRender : function(container, position){
24667         var m = [
24668              '<table cellspacing="0">',
24669                 '<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>',
24670                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24671         var dn = this.dayNames;
24672         for(var i = 0; i < 7; i++){
24673             var d = this.startDay+i;
24674             if(d > 6){
24675                 d = d-7;
24676             }
24677             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24678         }
24679         m[m.length] = "</tr></thead><tbody><tr>";
24680         for(var i = 0; i < 42; i++) {
24681             if(i % 7 == 0 && i != 0){
24682                 m[m.length] = "</tr><tr>";
24683             }
24684             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24685         }
24686         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24687             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24688
24689         var el = document.createElement("div");
24690         el.className = "x-date-picker";
24691         el.innerHTML = m.join("");
24692
24693         container.dom.insertBefore(el, position);
24694
24695         this.el = Roo.get(el);
24696         this.eventEl = Roo.get(el.firstChild);
24697
24698         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24699             handler: this.showPrevMonth,
24700             scope: this,
24701             preventDefault:true,
24702             stopDefault:true
24703         });
24704
24705         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24706             handler: this.showNextMonth,
24707             scope: this,
24708             preventDefault:true,
24709             stopDefault:true
24710         });
24711
24712         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24713
24714         this.monthPicker = this.el.down('div.x-date-mp');
24715         this.monthPicker.enableDisplayMode('block');
24716         
24717         var kn = new Roo.KeyNav(this.eventEl, {
24718             "left" : function(e){
24719                 e.ctrlKey ?
24720                     this.showPrevMonth() :
24721                     this.update(this.activeDate.add("d", -1));
24722             },
24723
24724             "right" : function(e){
24725                 e.ctrlKey ?
24726                     this.showNextMonth() :
24727                     this.update(this.activeDate.add("d", 1));
24728             },
24729
24730             "up" : function(e){
24731                 e.ctrlKey ?
24732                     this.showNextYear() :
24733                     this.update(this.activeDate.add("d", -7));
24734             },
24735
24736             "down" : function(e){
24737                 e.ctrlKey ?
24738                     this.showPrevYear() :
24739                     this.update(this.activeDate.add("d", 7));
24740             },
24741
24742             "pageUp" : function(e){
24743                 this.showNextMonth();
24744             },
24745
24746             "pageDown" : function(e){
24747                 this.showPrevMonth();
24748             },
24749
24750             "enter" : function(e){
24751                 e.stopPropagation();
24752                 return true;
24753             },
24754
24755             scope : this
24756         });
24757
24758         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24759
24760         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24761
24762         this.el.unselectable();
24763         
24764         this.cells = this.el.select("table.x-date-inner tbody td");
24765         this.textNodes = this.el.query("table.x-date-inner tbody span");
24766
24767         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24768             text: "&#160;",
24769             tooltip: this.monthYearText
24770         });
24771
24772         this.mbtn.on('click', this.showMonthPicker, this);
24773         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24774
24775
24776         var today = (new Date()).dateFormat(this.format);
24777         
24778         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24779         if (this.showClear) {
24780             baseTb.add( new Roo.Toolbar.Fill());
24781         }
24782         baseTb.add({
24783             text: String.format(this.todayText, today),
24784             tooltip: String.format(this.todayTip, today),
24785             handler: this.selectToday,
24786             scope: this
24787         });
24788         
24789         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24790             
24791         //});
24792         if (this.showClear) {
24793             
24794             baseTb.add( new Roo.Toolbar.Fill());
24795             baseTb.add({
24796                 text: '&#160;',
24797                 cls: 'x-btn-icon x-btn-clear',
24798                 handler: function() {
24799                     //this.value = '';
24800                     this.fireEvent("select", this, '');
24801                 },
24802                 scope: this
24803             });
24804         }
24805         
24806         
24807         if(Roo.isIE){
24808             this.el.repaint();
24809         }
24810         this.update(this.value);
24811     },
24812
24813     createMonthPicker : function(){
24814         if(!this.monthPicker.dom.firstChild){
24815             var buf = ['<table border="0" cellspacing="0">'];
24816             for(var i = 0; i < 6; i++){
24817                 buf.push(
24818                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24819                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24820                     i == 0 ?
24821                     '<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>' :
24822                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24823                 );
24824             }
24825             buf.push(
24826                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24827                     this.okText,
24828                     '</button><button type="button" class="x-date-mp-cancel">',
24829                     this.cancelText,
24830                     '</button></td></tr>',
24831                 '</table>'
24832             );
24833             this.monthPicker.update(buf.join(''));
24834             this.monthPicker.on('click', this.onMonthClick, this);
24835             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24836
24837             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24838             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24839
24840             this.mpMonths.each(function(m, a, i){
24841                 i += 1;
24842                 if((i%2) == 0){
24843                     m.dom.xmonth = 5 + Math.round(i * .5);
24844                 }else{
24845                     m.dom.xmonth = Math.round((i-1) * .5);
24846                 }
24847             });
24848         }
24849     },
24850
24851     showMonthPicker : function(){
24852         this.createMonthPicker();
24853         var size = this.el.getSize();
24854         this.monthPicker.setSize(size);
24855         this.monthPicker.child('table').setSize(size);
24856
24857         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24858         this.updateMPMonth(this.mpSelMonth);
24859         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24860         this.updateMPYear(this.mpSelYear);
24861
24862         this.monthPicker.slideIn('t', {duration:.2});
24863     },
24864
24865     updateMPYear : function(y){
24866         this.mpyear = y;
24867         var ys = this.mpYears.elements;
24868         for(var i = 1; i <= 10; i++){
24869             var td = ys[i-1], y2;
24870             if((i%2) == 0){
24871                 y2 = y + Math.round(i * .5);
24872                 td.firstChild.innerHTML = y2;
24873                 td.xyear = y2;
24874             }else{
24875                 y2 = y - (5-Math.round(i * .5));
24876                 td.firstChild.innerHTML = y2;
24877                 td.xyear = y2;
24878             }
24879             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24880         }
24881     },
24882
24883     updateMPMonth : function(sm){
24884         this.mpMonths.each(function(m, a, i){
24885             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24886         });
24887     },
24888
24889     selectMPMonth: function(m){
24890         
24891     },
24892
24893     onMonthClick : function(e, t){
24894         e.stopEvent();
24895         var el = new Roo.Element(t), pn;
24896         if(el.is('button.x-date-mp-cancel')){
24897             this.hideMonthPicker();
24898         }
24899         else if(el.is('button.x-date-mp-ok')){
24900             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24901             this.hideMonthPicker();
24902         }
24903         else if(pn = el.up('td.x-date-mp-month', 2)){
24904             this.mpMonths.removeClass('x-date-mp-sel');
24905             pn.addClass('x-date-mp-sel');
24906             this.mpSelMonth = pn.dom.xmonth;
24907         }
24908         else if(pn = el.up('td.x-date-mp-year', 2)){
24909             this.mpYears.removeClass('x-date-mp-sel');
24910             pn.addClass('x-date-mp-sel');
24911             this.mpSelYear = pn.dom.xyear;
24912         }
24913         else if(el.is('a.x-date-mp-prev')){
24914             this.updateMPYear(this.mpyear-10);
24915         }
24916         else if(el.is('a.x-date-mp-next')){
24917             this.updateMPYear(this.mpyear+10);
24918         }
24919     },
24920
24921     onMonthDblClick : function(e, t){
24922         e.stopEvent();
24923         var el = new Roo.Element(t), pn;
24924         if(pn = el.up('td.x-date-mp-month', 2)){
24925             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24926             this.hideMonthPicker();
24927         }
24928         else if(pn = el.up('td.x-date-mp-year', 2)){
24929             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24930             this.hideMonthPicker();
24931         }
24932     },
24933
24934     hideMonthPicker : function(disableAnim){
24935         if(this.monthPicker){
24936             if(disableAnim === true){
24937                 this.monthPicker.hide();
24938             }else{
24939                 this.monthPicker.slideOut('t', {duration:.2});
24940             }
24941         }
24942     },
24943
24944     // private
24945     showPrevMonth : function(e){
24946         this.update(this.activeDate.add("mo", -1));
24947     },
24948
24949     // private
24950     showNextMonth : function(e){
24951         this.update(this.activeDate.add("mo", 1));
24952     },
24953
24954     // private
24955     showPrevYear : function(){
24956         this.update(this.activeDate.add("y", -1));
24957     },
24958
24959     // private
24960     showNextYear : function(){
24961         this.update(this.activeDate.add("y", 1));
24962     },
24963
24964     // private
24965     handleMouseWheel : function(e){
24966         var delta = e.getWheelDelta();
24967         if(delta > 0){
24968             this.showPrevMonth();
24969             e.stopEvent();
24970         } else if(delta < 0){
24971             this.showNextMonth();
24972             e.stopEvent();
24973         }
24974     },
24975
24976     // private
24977     handleDateClick : function(e, t){
24978         e.stopEvent();
24979         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24980             this.setValue(new Date(t.dateValue));
24981             this.fireEvent("select", this, this.value);
24982         }
24983     },
24984
24985     // private
24986     selectToday : function(){
24987         this.setValue(new Date().clearTime());
24988         this.fireEvent("select", this, this.value);
24989     },
24990
24991     // private
24992     update : function(date){
24993         var vd = this.activeDate;
24994         this.activeDate = date;
24995         if(vd && this.el){
24996             var t = date.getTime();
24997             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24998                 this.cells.removeClass("x-date-selected");
24999                 this.cells.each(function(c){
25000                    if(c.dom.firstChild.dateValue == t){
25001                        c.addClass("x-date-selected");
25002                        setTimeout(function(){
25003                             try{c.dom.firstChild.focus();}catch(e){}
25004                        }, 50);
25005                        return false;
25006                    }
25007                 });
25008                 return;
25009             }
25010         }
25011         var days = date.getDaysInMonth();
25012         var firstOfMonth = date.getFirstDateOfMonth();
25013         var startingPos = firstOfMonth.getDay()-this.startDay;
25014
25015         if(startingPos <= this.startDay){
25016             startingPos += 7;
25017         }
25018
25019         var pm = date.add("mo", -1);
25020         var prevStart = pm.getDaysInMonth()-startingPos;
25021
25022         var cells = this.cells.elements;
25023         var textEls = this.textNodes;
25024         days += startingPos;
25025
25026         // convert everything to numbers so it's fast
25027         var day = 86400000;
25028         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25029         var today = new Date().clearTime().getTime();
25030         var sel = date.clearTime().getTime();
25031         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25032         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25033         var ddMatch = this.disabledDatesRE;
25034         var ddText = this.disabledDatesText;
25035         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25036         var ddaysText = this.disabledDaysText;
25037         var format = this.format;
25038
25039         var setCellClass = function(cal, cell){
25040             cell.title = "";
25041             var t = d.getTime();
25042             cell.firstChild.dateValue = t;
25043             if(t == today){
25044                 cell.className += " x-date-today";
25045                 cell.title = cal.todayText;
25046             }
25047             if(t == sel){
25048                 cell.className += " x-date-selected";
25049                 setTimeout(function(){
25050                     try{cell.firstChild.focus();}catch(e){}
25051                 }, 50);
25052             }
25053             // disabling
25054             if(t < min) {
25055                 cell.className = " x-date-disabled";
25056                 cell.title = cal.minText;
25057                 return;
25058             }
25059             if(t > max) {
25060                 cell.className = " x-date-disabled";
25061                 cell.title = cal.maxText;
25062                 return;
25063             }
25064             if(ddays){
25065                 if(ddays.indexOf(d.getDay()) != -1){
25066                     cell.title = ddaysText;
25067                     cell.className = " x-date-disabled";
25068                 }
25069             }
25070             if(ddMatch && format){
25071                 var fvalue = d.dateFormat(format);
25072                 if(ddMatch.test(fvalue)){
25073                     cell.title = ddText.replace("%0", fvalue);
25074                     cell.className = " x-date-disabled";
25075                 }
25076             }
25077         };
25078
25079         var i = 0;
25080         for(; i < startingPos; i++) {
25081             textEls[i].innerHTML = (++prevStart);
25082             d.setDate(d.getDate()+1);
25083             cells[i].className = "x-date-prevday";
25084             setCellClass(this, cells[i]);
25085         }
25086         for(; i < days; i++){
25087             intDay = i - startingPos + 1;
25088             textEls[i].innerHTML = (intDay);
25089             d.setDate(d.getDate()+1);
25090             cells[i].className = "x-date-active";
25091             setCellClass(this, cells[i]);
25092         }
25093         var extraDays = 0;
25094         for(; i < 42; i++) {
25095              textEls[i].innerHTML = (++extraDays);
25096              d.setDate(d.getDate()+1);
25097              cells[i].className = "x-date-nextday";
25098              setCellClass(this, cells[i]);
25099         }
25100
25101         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25102
25103         if(!this.internalRender){
25104             var main = this.el.dom.firstChild;
25105             var w = main.offsetWidth;
25106             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25107             Roo.fly(main).setWidth(w);
25108             this.internalRender = true;
25109             // opera does not respect the auto grow header center column
25110             // then, after it gets a width opera refuses to recalculate
25111             // without a second pass
25112             if(Roo.isOpera && !this.secondPass){
25113                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25114                 this.secondPass = true;
25115                 this.update.defer(10, this, [date]);
25116             }
25117         }
25118     }
25119 });/*
25120  * Based on:
25121  * Ext JS Library 1.1.1
25122  * Copyright(c) 2006-2007, Ext JS, LLC.
25123  *
25124  * Originally Released Under LGPL - original licence link has changed is not relivant.
25125  *
25126  * Fork - LGPL
25127  * <script type="text/javascript">
25128  */
25129 /**
25130  * @class Roo.TabPanel
25131  * @extends Roo.util.Observable
25132  * A lightweight tab container.
25133  * <br><br>
25134  * Usage:
25135  * <pre><code>
25136 // basic tabs 1, built from existing content
25137 var tabs = new Roo.TabPanel("tabs1");
25138 tabs.addTab("script", "View Script");
25139 tabs.addTab("markup", "View Markup");
25140 tabs.activate("script");
25141
25142 // more advanced tabs, built from javascript
25143 var jtabs = new Roo.TabPanel("jtabs");
25144 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25145
25146 // set up the UpdateManager
25147 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25148 var updater = tab2.getUpdateManager();
25149 updater.setDefaultUrl("ajax1.htm");
25150 tab2.on('activate', updater.refresh, updater, true);
25151
25152 // Use setUrl for Ajax loading
25153 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25154 tab3.setUrl("ajax2.htm", null, true);
25155
25156 // Disabled tab
25157 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25158 tab4.disable();
25159
25160 jtabs.activate("jtabs-1");
25161  * </code></pre>
25162  * @constructor
25163  * Create a new TabPanel.
25164  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25165  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25166  */
25167 Roo.TabPanel = function(container, config){
25168     /**
25169     * The container element for this TabPanel.
25170     * @type Roo.Element
25171     */
25172     this.el = Roo.get(container, true);
25173     if(config){
25174         if(typeof config == "boolean"){
25175             this.tabPosition = config ? "bottom" : "top";
25176         }else{
25177             Roo.apply(this, config);
25178         }
25179     }
25180     if(this.tabPosition == "bottom"){
25181         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25182         this.el.addClass("x-tabs-bottom");
25183     }
25184     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25185     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25186     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25187     if(Roo.isIE){
25188         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25189     }
25190     if(this.tabPosition != "bottom"){
25191     /** The body element that contains {@link Roo.TabPanelItem} bodies.
25192      * @type Roo.Element
25193      */
25194       this.bodyEl = Roo.get(this.createBody(this.el.dom));
25195       this.el.addClass("x-tabs-top");
25196     }
25197     this.items = [];
25198
25199     this.bodyEl.setStyle("position", "relative");
25200
25201     this.active = null;
25202     this.activateDelegate = this.activate.createDelegate(this);
25203
25204     this.addEvents({
25205         /**
25206          * @event tabchange
25207          * Fires when the active tab changes
25208          * @param {Roo.TabPanel} this
25209          * @param {Roo.TabPanelItem} activePanel The new active tab
25210          */
25211         "tabchange": true,
25212         /**
25213          * @event beforetabchange
25214          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25215          * @param {Roo.TabPanel} this
25216          * @param {Object} e Set cancel to true on this object to cancel the tab change
25217          * @param {Roo.TabPanelItem} tab The tab being changed to
25218          */
25219         "beforetabchange" : true
25220     });
25221
25222     Roo.EventManager.onWindowResize(this.onResize, this);
25223     this.cpad = this.el.getPadding("lr");
25224     this.hiddenCount = 0;
25225
25226     Roo.TabPanel.superclass.constructor.call(this);
25227 };
25228
25229 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25230         /*
25231          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25232          */
25233     tabPosition : "top",
25234         /*
25235          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25236          */
25237     currentTabWidth : 0,
25238         /*
25239          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25240          */
25241     minTabWidth : 40,
25242         /*
25243          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25244          */
25245     maxTabWidth : 250,
25246         /*
25247          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25248          */
25249     preferredTabWidth : 175,
25250         /*
25251          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25252          */
25253     resizeTabs : false,
25254         /*
25255          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25256          */
25257     monitorResize : true,
25258
25259     /**
25260      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25261      * @param {String} id The id of the div to use <b>or create</b>
25262      * @param {String} text The text for the tab
25263      * @param {String} content (optional) Content to put in the TabPanelItem body
25264      * @param {Boolean} closable (optional) True to create a close icon on the tab
25265      * @return {Roo.TabPanelItem} The created TabPanelItem
25266      */
25267     addTab : function(id, text, content, closable){
25268         var item = new Roo.TabPanelItem(this, id, text, closable);
25269         this.addTabItem(item);
25270         if(content){
25271             item.setContent(content);
25272         }
25273         return item;
25274     },
25275
25276     /**
25277      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25278      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25279      * @return {Roo.TabPanelItem}
25280      */
25281     getTab : function(id){
25282         return this.items[id];
25283     },
25284
25285     /**
25286      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25287      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25288      */
25289     hideTab : function(id){
25290         var t = this.items[id];
25291         if(!t.isHidden()){
25292            t.setHidden(true);
25293            this.hiddenCount++;
25294            this.autoSizeTabs();
25295         }
25296     },
25297
25298     /**
25299      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25300      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25301      */
25302     unhideTab : function(id){
25303         var t = this.items[id];
25304         if(t.isHidden()){
25305            t.setHidden(false);
25306            this.hiddenCount--;
25307            this.autoSizeTabs();
25308         }
25309     },
25310
25311     /**
25312      * Adds an existing {@link Roo.TabPanelItem}.
25313      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25314      */
25315     addTabItem : function(item){
25316         this.items[item.id] = item;
25317         this.items.push(item);
25318         if(this.resizeTabs){
25319            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25320            this.autoSizeTabs();
25321         }else{
25322             item.autoSize();
25323         }
25324     },
25325
25326     /**
25327      * Removes a {@link Roo.TabPanelItem}.
25328      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25329      */
25330     removeTab : function(id){
25331         var items = this.items;
25332         var tab = items[id];
25333         if(!tab) { return; }
25334         var index = items.indexOf(tab);
25335         if(this.active == tab && items.length > 1){
25336             var newTab = this.getNextAvailable(index);
25337             if(newTab) {
25338                 newTab.activate();
25339             }
25340         }
25341         this.stripEl.dom.removeChild(tab.pnode.dom);
25342         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25343             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25344         }
25345         items.splice(index, 1);
25346         delete this.items[tab.id];
25347         tab.fireEvent("close", tab);
25348         tab.purgeListeners();
25349         this.autoSizeTabs();
25350     },
25351
25352     getNextAvailable : function(start){
25353         var items = this.items;
25354         var index = start;
25355         // look for a next tab that will slide over to
25356         // replace the one being removed
25357         while(index < items.length){
25358             var item = items[++index];
25359             if(item && !item.isHidden()){
25360                 return item;
25361             }
25362         }
25363         // if one isn't found select the previous tab (on the left)
25364         index = start;
25365         while(index >= 0){
25366             var item = items[--index];
25367             if(item && !item.isHidden()){
25368                 return item;
25369             }
25370         }
25371         return null;
25372     },
25373
25374     /**
25375      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25376      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25377      */
25378     disableTab : function(id){
25379         var tab = this.items[id];
25380         if(tab && this.active != tab){
25381             tab.disable();
25382         }
25383     },
25384
25385     /**
25386      * Enables a {@link Roo.TabPanelItem} that is disabled.
25387      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25388      */
25389     enableTab : function(id){
25390         var tab = this.items[id];
25391         tab.enable();
25392     },
25393
25394     /**
25395      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25396      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25397      * @return {Roo.TabPanelItem} The TabPanelItem.
25398      */
25399     activate : function(id){
25400         var tab = this.items[id];
25401         if(!tab){
25402             return null;
25403         }
25404         if(tab == this.active || tab.disabled){
25405             return tab;
25406         }
25407         var e = {};
25408         this.fireEvent("beforetabchange", this, e, tab);
25409         if(e.cancel !== true && !tab.disabled){
25410             if(this.active){
25411                 this.active.hide();
25412             }
25413             this.active = this.items[id];
25414             this.active.show();
25415             this.fireEvent("tabchange", this, this.active);
25416         }
25417         return tab;
25418     },
25419
25420     /**
25421      * Gets the active {@link Roo.TabPanelItem}.
25422      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25423      */
25424     getActiveTab : function(){
25425         return this.active;
25426     },
25427
25428     /**
25429      * Updates the tab body element to fit the height of the container element
25430      * for overflow scrolling
25431      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25432      */
25433     syncHeight : function(targetHeight){
25434         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25435         var bm = this.bodyEl.getMargins();
25436         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25437         this.bodyEl.setHeight(newHeight);
25438         return newHeight;
25439     },
25440
25441     onResize : function(){
25442         if(this.monitorResize){
25443             this.autoSizeTabs();
25444         }
25445     },
25446
25447     /**
25448      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25449      */
25450     beginUpdate : function(){
25451         this.updating = true;
25452     },
25453
25454     /**
25455      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25456      */
25457     endUpdate : function(){
25458         this.updating = false;
25459         this.autoSizeTabs();
25460     },
25461
25462     /**
25463      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25464      */
25465     autoSizeTabs : function(){
25466         var count = this.items.length;
25467         var vcount = count - this.hiddenCount;
25468         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25469         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25470         var availWidth = Math.floor(w / vcount);
25471         var b = this.stripBody;
25472         if(b.getWidth() > w){
25473             var tabs = this.items;
25474             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25475             if(availWidth < this.minTabWidth){
25476                 /*if(!this.sleft){    // incomplete scrolling code
25477                     this.createScrollButtons();
25478                 }
25479                 this.showScroll();
25480                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25481             }
25482         }else{
25483             if(this.currentTabWidth < this.preferredTabWidth){
25484                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25485             }
25486         }
25487     },
25488
25489     /**
25490      * Returns the number of tabs in this TabPanel.
25491      * @return {Number}
25492      */
25493      getCount : function(){
25494          return this.items.length;
25495      },
25496
25497     /**
25498      * Resizes all the tabs to the passed width
25499      * @param {Number} The new width
25500      */
25501     setTabWidth : function(width){
25502         this.currentTabWidth = width;
25503         for(var i = 0, len = this.items.length; i < len; i++) {
25504                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25505         }
25506     },
25507
25508     /**
25509      * Destroys this TabPanel
25510      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25511      */
25512     destroy : function(removeEl){
25513         Roo.EventManager.removeResizeListener(this.onResize, this);
25514         for(var i = 0, len = this.items.length; i < len; i++){
25515             this.items[i].purgeListeners();
25516         }
25517         if(removeEl === true){
25518             this.el.update("");
25519             this.el.remove();
25520         }
25521     }
25522 });
25523
25524 /**
25525  * @class Roo.TabPanelItem
25526  * @extends Roo.util.Observable
25527  * Represents an individual item (tab plus body) in a TabPanel.
25528  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25529  * @param {String} id The id of this TabPanelItem
25530  * @param {String} text The text for the tab of this TabPanelItem
25531  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25532  */
25533 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25534     /**
25535      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25536      * @type Roo.TabPanel
25537      */
25538     this.tabPanel = tabPanel;
25539     /**
25540      * The id for this TabPanelItem
25541      * @type String
25542      */
25543     this.id = id;
25544     /** @private */
25545     this.disabled = false;
25546     /** @private */
25547     this.text = text;
25548     /** @private */
25549     this.loaded = false;
25550     this.closable = closable;
25551
25552     /**
25553      * The body element for this TabPanelItem.
25554      * @type Roo.Element
25555      */
25556     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25557     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25558     this.bodyEl.setStyle("display", "block");
25559     this.bodyEl.setStyle("zoom", "1");
25560     this.hideAction();
25561
25562     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25563     /** @private */
25564     this.el = Roo.get(els.el, true);
25565     this.inner = Roo.get(els.inner, true);
25566     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25567     this.pnode = Roo.get(els.el.parentNode, true);
25568     this.el.on("mousedown", this.onTabMouseDown, this);
25569     this.el.on("click", this.onTabClick, this);
25570     /** @private */
25571     if(closable){
25572         var c = Roo.get(els.close, true);
25573         c.dom.title = this.closeText;
25574         c.addClassOnOver("close-over");
25575         c.on("click", this.closeClick, this);
25576      }
25577
25578     this.addEvents({
25579          /**
25580          * @event activate
25581          * Fires when this tab becomes the active tab.
25582          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25583          * @param {Roo.TabPanelItem} this
25584          */
25585         "activate": true,
25586         /**
25587          * @event beforeclose
25588          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25589          * @param {Roo.TabPanelItem} this
25590          * @param {Object} e Set cancel to true on this object to cancel the close.
25591          */
25592         "beforeclose": true,
25593         /**
25594          * @event close
25595          * Fires when this tab is closed.
25596          * @param {Roo.TabPanelItem} this
25597          */
25598          "close": true,
25599         /**
25600          * @event deactivate
25601          * Fires when this tab is no longer the active tab.
25602          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25603          * @param {Roo.TabPanelItem} this
25604          */
25605          "deactivate" : true
25606     });
25607     this.hidden = false;
25608
25609     Roo.TabPanelItem.superclass.constructor.call(this);
25610 };
25611
25612 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25613     purgeListeners : function(){
25614        Roo.util.Observable.prototype.purgeListeners.call(this);
25615        this.el.removeAllListeners();
25616     },
25617     /**
25618      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25619      */
25620     show : function(){
25621         this.pnode.addClass("on");
25622         this.showAction();
25623         if(Roo.isOpera){
25624             this.tabPanel.stripWrap.repaint();
25625         }
25626         this.fireEvent("activate", this.tabPanel, this);
25627     },
25628
25629     /**
25630      * Returns true if this tab is the active tab.
25631      * @return {Boolean}
25632      */
25633     isActive : function(){
25634         return this.tabPanel.getActiveTab() == this;
25635     },
25636
25637     /**
25638      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25639      */
25640     hide : function(){
25641         this.pnode.removeClass("on");
25642         this.hideAction();
25643         this.fireEvent("deactivate", this.tabPanel, this);
25644     },
25645
25646     hideAction : function(){
25647         this.bodyEl.hide();
25648         this.bodyEl.setStyle("position", "absolute");
25649         this.bodyEl.setLeft("-20000px");
25650         this.bodyEl.setTop("-20000px");
25651     },
25652
25653     showAction : function(){
25654         this.bodyEl.setStyle("position", "relative");
25655         this.bodyEl.setTop("");
25656         this.bodyEl.setLeft("");
25657         this.bodyEl.show();
25658     },
25659
25660     /**
25661      * Set the tooltip for the tab.
25662      * @param {String} tooltip The tab's tooltip
25663      */
25664     setTooltip : function(text){
25665         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25666             this.textEl.dom.qtip = text;
25667             this.textEl.dom.removeAttribute('title');
25668         }else{
25669             this.textEl.dom.title = text;
25670         }
25671     },
25672
25673     onTabClick : function(e){
25674         e.preventDefault();
25675         this.tabPanel.activate(this.id);
25676     },
25677
25678     onTabMouseDown : function(e){
25679         e.preventDefault();
25680         this.tabPanel.activate(this.id);
25681     },
25682
25683     getWidth : function(){
25684         return this.inner.getWidth();
25685     },
25686
25687     setWidth : function(width){
25688         var iwidth = width - this.pnode.getPadding("lr");
25689         this.inner.setWidth(iwidth);
25690         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25691         this.pnode.setWidth(width);
25692     },
25693
25694     /**
25695      * Show or hide the tab
25696      * @param {Boolean} hidden True to hide or false to show.
25697      */
25698     setHidden : function(hidden){
25699         this.hidden = hidden;
25700         this.pnode.setStyle("display", hidden ? "none" : "");
25701     },
25702
25703     /**
25704      * Returns true if this tab is "hidden"
25705      * @return {Boolean}
25706      */
25707     isHidden : function(){
25708         return this.hidden;
25709     },
25710
25711     /**
25712      * Returns the text for this tab
25713      * @return {String}
25714      */
25715     getText : function(){
25716         return this.text;
25717     },
25718
25719     autoSize : function(){
25720         //this.el.beginMeasure();
25721         this.textEl.setWidth(1);
25722         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25723         //this.el.endMeasure();
25724     },
25725
25726     /**
25727      * Sets the text for the tab (Note: this also sets the tooltip text)
25728      * @param {String} text The tab's text and tooltip
25729      */
25730     setText : function(text){
25731         this.text = text;
25732         this.textEl.update(text);
25733         this.setTooltip(text);
25734         if(!this.tabPanel.resizeTabs){
25735             this.autoSize();
25736         }
25737     },
25738     /**
25739      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25740      */
25741     activate : function(){
25742         this.tabPanel.activate(this.id);
25743     },
25744
25745     /**
25746      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25747      */
25748     disable : function(){
25749         if(this.tabPanel.active != this){
25750             this.disabled = true;
25751             this.pnode.addClass("disabled");
25752         }
25753     },
25754
25755     /**
25756      * Enables this TabPanelItem if it was previously disabled.
25757      */
25758     enable : function(){
25759         this.disabled = false;
25760         this.pnode.removeClass("disabled");
25761     },
25762
25763     /**
25764      * Sets the content for this TabPanelItem.
25765      * @param {String} content The content
25766      * @param {Boolean} loadScripts true to look for and load scripts
25767      */
25768     setContent : function(content, loadScripts){
25769         this.bodyEl.update(content, loadScripts);
25770     },
25771
25772     /**
25773      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25774      * @return {Roo.UpdateManager} The UpdateManager
25775      */
25776     getUpdateManager : function(){
25777         return this.bodyEl.getUpdateManager();
25778     },
25779
25780     /**
25781      * Set a URL to be used to load the content for this TabPanelItem.
25782      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25783      * @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)
25784      * @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)
25785      * @return {Roo.UpdateManager} The UpdateManager
25786      */
25787     setUrl : function(url, params, loadOnce){
25788         if(this.refreshDelegate){
25789             this.un('activate', this.refreshDelegate);
25790         }
25791         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25792         this.on("activate", this.refreshDelegate);
25793         return this.bodyEl.getUpdateManager();
25794     },
25795
25796     /** @private */
25797     _handleRefresh : function(url, params, loadOnce){
25798         if(!loadOnce || !this.loaded){
25799             var updater = this.bodyEl.getUpdateManager();
25800             updater.update(url, params, this._setLoaded.createDelegate(this));
25801         }
25802     },
25803
25804     /**
25805      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25806      *   Will fail silently if the setUrl method has not been called.
25807      *   This does not activate the panel, just updates its content.
25808      */
25809     refresh : function(){
25810         if(this.refreshDelegate){
25811            this.loaded = false;
25812            this.refreshDelegate();
25813         }
25814     },
25815
25816     /** @private */
25817     _setLoaded : function(){
25818         this.loaded = true;
25819     },
25820
25821     /** @private */
25822     closeClick : function(e){
25823         var o = {};
25824         e.stopEvent();
25825         this.fireEvent("beforeclose", this, o);
25826         if(o.cancel !== true){
25827             this.tabPanel.removeTab(this.id);
25828         }
25829     },
25830     /**
25831      * The text displayed in the tooltip for the close icon.
25832      * @type String
25833      */
25834     closeText : "Close this tab"
25835 });
25836
25837 /** @private */
25838 Roo.TabPanel.prototype.createStrip = function(container){
25839     var strip = document.createElement("div");
25840     strip.className = "x-tabs-wrap";
25841     container.appendChild(strip);
25842     return strip;
25843 };
25844 /** @private */
25845 Roo.TabPanel.prototype.createStripList = function(strip){
25846     // div wrapper for retard IE
25847     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
25848     return strip.firstChild.firstChild.firstChild.firstChild;
25849 };
25850 /** @private */
25851 Roo.TabPanel.prototype.createBody = function(container){
25852     var body = document.createElement("div");
25853     Roo.id(body, "tab-body");
25854     Roo.fly(body).addClass("x-tabs-body");
25855     container.appendChild(body);
25856     return body;
25857 };
25858 /** @private */
25859 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25860     var body = Roo.getDom(id);
25861     if(!body){
25862         body = document.createElement("div");
25863         body.id = id;
25864     }
25865     Roo.fly(body).addClass("x-tabs-item-body");
25866     bodyEl.insertBefore(body, bodyEl.firstChild);
25867     return body;
25868 };
25869 /** @private */
25870 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25871     var td = document.createElement("td");
25872     stripEl.appendChild(td);
25873     if(closable){
25874         td.className = "x-tabs-closable";
25875         if(!this.closeTpl){
25876             this.closeTpl = new Roo.Template(
25877                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25878                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25879                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25880             );
25881         }
25882         var el = this.closeTpl.overwrite(td, {"text": text});
25883         var close = el.getElementsByTagName("div")[0];
25884         var inner = el.getElementsByTagName("em")[0];
25885         return {"el": el, "close": close, "inner": inner};
25886     } else {
25887         if(!this.tabTpl){
25888             this.tabTpl = new Roo.Template(
25889                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25890                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25891             );
25892         }
25893         var el = this.tabTpl.overwrite(td, {"text": text});
25894         var inner = el.getElementsByTagName("em")[0];
25895         return {"el": el, "inner": inner};
25896     }
25897 };/*
25898  * Based on:
25899  * Ext JS Library 1.1.1
25900  * Copyright(c) 2006-2007, Ext JS, LLC.
25901  *
25902  * Originally Released Under LGPL - original licence link has changed is not relivant.
25903  *
25904  * Fork - LGPL
25905  * <script type="text/javascript">
25906  */
25907
25908 /**
25909  * @class Roo.Button
25910  * @extends Roo.util.Observable
25911  * Simple Button class
25912  * @cfg {String} text The button text
25913  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25914  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25915  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25916  * @cfg {Object} scope The scope of the handler
25917  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25918  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25919  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25920  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25921  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25922  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25923    applies if enableToggle = true)
25924  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25925  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25926   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25927  * @constructor
25928  * Create a new button
25929  * @param {Object} config The config object
25930  */
25931 Roo.Button = function(renderTo, config)
25932 {
25933     if (!config) {
25934         config = renderTo;
25935         renderTo = config.renderTo || false;
25936     }
25937     
25938     Roo.apply(this, config);
25939     this.addEvents({
25940         /**
25941              * @event click
25942              * Fires when this button is clicked
25943              * @param {Button} this
25944              * @param {EventObject} e The click event
25945              */
25946             "click" : true,
25947         /**
25948              * @event toggle
25949              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25950              * @param {Button} this
25951              * @param {Boolean} pressed
25952              */
25953             "toggle" : true,
25954         /**
25955              * @event mouseover
25956              * Fires when the mouse hovers over the button
25957              * @param {Button} this
25958              * @param {Event} e The event object
25959              */
25960         'mouseover' : true,
25961         /**
25962              * @event mouseout
25963              * Fires when the mouse exits the button
25964              * @param {Button} this
25965              * @param {Event} e The event object
25966              */
25967         'mouseout': true,
25968          /**
25969              * @event render
25970              * Fires when the button is rendered
25971              * @param {Button} this
25972              */
25973         'render': true
25974     });
25975     if(this.menu){
25976         this.menu = Roo.menu.MenuMgr.get(this.menu);
25977     }
25978     // register listeners first!!  - so render can be captured..
25979     Roo.util.Observable.call(this);
25980     if(renderTo){
25981         this.render(renderTo);
25982     }
25983     
25984   
25985 };
25986
25987 Roo.extend(Roo.Button, Roo.util.Observable, {
25988     /**
25989      * 
25990      */
25991     
25992     /**
25993      * Read-only. True if this button is hidden
25994      * @type Boolean
25995      */
25996     hidden : false,
25997     /**
25998      * Read-only. True if this button is disabled
25999      * @type Boolean
26000      */
26001     disabled : false,
26002     /**
26003      * Read-only. True if this button is pressed (only if enableToggle = true)
26004      * @type Boolean
26005      */
26006     pressed : false,
26007
26008     /**
26009      * @cfg {Number} tabIndex 
26010      * The DOM tabIndex for this button (defaults to undefined)
26011      */
26012     tabIndex : undefined,
26013
26014     /**
26015      * @cfg {Boolean} enableToggle
26016      * True to enable pressed/not pressed toggling (defaults to false)
26017      */
26018     enableToggle: false,
26019     /**
26020      * @cfg {Mixed} menu
26021      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26022      */
26023     menu : undefined,
26024     /**
26025      * @cfg {String} menuAlign
26026      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26027      */
26028     menuAlign : "tl-bl?",
26029
26030     /**
26031      * @cfg {String} iconCls
26032      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26033      */
26034     iconCls : undefined,
26035     /**
26036      * @cfg {String} type
26037      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26038      */
26039     type : 'button',
26040
26041     // private
26042     menuClassTarget: 'tr',
26043
26044     /**
26045      * @cfg {String} clickEvent
26046      * The type of event to map to the button's event handler (defaults to 'click')
26047      */
26048     clickEvent : 'click',
26049
26050     /**
26051      * @cfg {Boolean} handleMouseEvents
26052      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26053      */
26054     handleMouseEvents : true,
26055
26056     /**
26057      * @cfg {String} tooltipType
26058      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26059      */
26060     tooltipType : 'qtip',
26061
26062     /**
26063      * @cfg {String} cls
26064      * A CSS class to apply to the button's main element.
26065      */
26066     
26067     /**
26068      * @cfg {Roo.Template} template (Optional)
26069      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26070      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26071      * require code modifications if required elements (e.g. a button) aren't present.
26072      */
26073
26074     // private
26075     render : function(renderTo){
26076         var btn;
26077         if(this.hideParent){
26078             this.parentEl = Roo.get(renderTo);
26079         }
26080         if(!this.dhconfig){
26081             if(!this.template){
26082                 if(!Roo.Button.buttonTemplate){
26083                     // hideous table template
26084                     Roo.Button.buttonTemplate = new Roo.Template(
26085                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26086                         '<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>',
26087                         "</tr></tbody></table>");
26088                 }
26089                 this.template = Roo.Button.buttonTemplate;
26090             }
26091             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26092             var btnEl = btn.child("button:first");
26093             btnEl.on('focus', this.onFocus, this);
26094             btnEl.on('blur', this.onBlur, this);
26095             if(this.cls){
26096                 btn.addClass(this.cls);
26097             }
26098             if(this.icon){
26099                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26100             }
26101             if(this.iconCls){
26102                 btnEl.addClass(this.iconCls);
26103                 if(!this.cls){
26104                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26105                 }
26106             }
26107             if(this.tabIndex !== undefined){
26108                 btnEl.dom.tabIndex = this.tabIndex;
26109             }
26110             if(this.tooltip){
26111                 if(typeof this.tooltip == 'object'){
26112                     Roo.QuickTips.tips(Roo.apply({
26113                           target: btnEl.id
26114                     }, this.tooltip));
26115                 } else {
26116                     btnEl.dom[this.tooltipType] = this.tooltip;
26117                 }
26118             }
26119         }else{
26120             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26121         }
26122         this.el = btn;
26123         if(this.id){
26124             this.el.dom.id = this.el.id = this.id;
26125         }
26126         if(this.menu){
26127             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26128             this.menu.on("show", this.onMenuShow, this);
26129             this.menu.on("hide", this.onMenuHide, this);
26130         }
26131         btn.addClass("x-btn");
26132         if(Roo.isIE && !Roo.isIE7){
26133             this.autoWidth.defer(1, this);
26134         }else{
26135             this.autoWidth();
26136         }
26137         if(this.handleMouseEvents){
26138             btn.on("mouseover", this.onMouseOver, this);
26139             btn.on("mouseout", this.onMouseOut, this);
26140             btn.on("mousedown", this.onMouseDown, this);
26141         }
26142         btn.on(this.clickEvent, this.onClick, this);
26143         //btn.on("mouseup", this.onMouseUp, this);
26144         if(this.hidden){
26145             this.hide();
26146         }
26147         if(this.disabled){
26148             this.disable();
26149         }
26150         Roo.ButtonToggleMgr.register(this);
26151         if(this.pressed){
26152             this.el.addClass("x-btn-pressed");
26153         }
26154         if(this.repeat){
26155             var repeater = new Roo.util.ClickRepeater(btn,
26156                 typeof this.repeat == "object" ? this.repeat : {}
26157             );
26158             repeater.on("click", this.onClick,  this);
26159         }
26160         
26161         this.fireEvent('render', this);
26162         
26163     },
26164     /**
26165      * Returns the button's underlying element
26166      * @return {Roo.Element} The element
26167      */
26168     getEl : function(){
26169         return this.el;  
26170     },
26171     
26172     /**
26173      * Destroys this Button and removes any listeners.
26174      */
26175     destroy : function(){
26176         Roo.ButtonToggleMgr.unregister(this);
26177         this.el.removeAllListeners();
26178         this.purgeListeners();
26179         this.el.remove();
26180     },
26181
26182     // private
26183     autoWidth : function(){
26184         if(this.el){
26185             this.el.setWidth("auto");
26186             if(Roo.isIE7 && Roo.isStrict){
26187                 var ib = this.el.child('button');
26188                 if(ib && ib.getWidth() > 20){
26189                     ib.clip();
26190                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26191                 }
26192             }
26193             if(this.minWidth){
26194                 if(this.hidden){
26195                     this.el.beginMeasure();
26196                 }
26197                 if(this.el.getWidth() < this.minWidth){
26198                     this.el.setWidth(this.minWidth);
26199                 }
26200                 if(this.hidden){
26201                     this.el.endMeasure();
26202                 }
26203             }
26204         }
26205     },
26206
26207     /**
26208      * Assigns this button's click handler
26209      * @param {Function} handler The function to call when the button is clicked
26210      * @param {Object} scope (optional) Scope for the function passed in
26211      */
26212     setHandler : function(handler, scope){
26213         this.handler = handler;
26214         this.scope = scope;  
26215     },
26216     
26217     /**
26218      * Sets this button's text
26219      * @param {String} text The button text
26220      */
26221     setText : function(text){
26222         this.text = text;
26223         if(this.el){
26224             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26225         }
26226         this.autoWidth();
26227     },
26228     
26229     /**
26230      * Gets the text for this button
26231      * @return {String} The button text
26232      */
26233     getText : function(){
26234         return this.text;  
26235     },
26236     
26237     /**
26238      * Show this button
26239      */
26240     show: function(){
26241         this.hidden = false;
26242         if(this.el){
26243             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26244         }
26245     },
26246     
26247     /**
26248      * Hide this button
26249      */
26250     hide: function(){
26251         this.hidden = true;
26252         if(this.el){
26253             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26254         }
26255     },
26256     
26257     /**
26258      * Convenience function for boolean show/hide
26259      * @param {Boolean} visible True to show, false to hide
26260      */
26261     setVisible: function(visible){
26262         if(visible) {
26263             this.show();
26264         }else{
26265             this.hide();
26266         }
26267     },
26268     
26269     /**
26270      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26271      * @param {Boolean} state (optional) Force a particular state
26272      */
26273     toggle : function(state){
26274         state = state === undefined ? !this.pressed : state;
26275         if(state != this.pressed){
26276             if(state){
26277                 this.el.addClass("x-btn-pressed");
26278                 this.pressed = true;
26279                 this.fireEvent("toggle", this, true);
26280             }else{
26281                 this.el.removeClass("x-btn-pressed");
26282                 this.pressed = false;
26283                 this.fireEvent("toggle", this, false);
26284             }
26285             if(this.toggleHandler){
26286                 this.toggleHandler.call(this.scope || this, this, state);
26287             }
26288         }
26289     },
26290     
26291     /**
26292      * Focus the button
26293      */
26294     focus : function(){
26295         this.el.child('button:first').focus();
26296     },
26297     
26298     /**
26299      * Disable this button
26300      */
26301     disable : function(){
26302         if(this.el){
26303             this.el.addClass("x-btn-disabled");
26304         }
26305         this.disabled = true;
26306     },
26307     
26308     /**
26309      * Enable this button
26310      */
26311     enable : function(){
26312         if(this.el){
26313             this.el.removeClass("x-btn-disabled");
26314         }
26315         this.disabled = false;
26316     },
26317
26318     /**
26319      * Convenience function for boolean enable/disable
26320      * @param {Boolean} enabled True to enable, false to disable
26321      */
26322     setDisabled : function(v){
26323         this[v !== true ? "enable" : "disable"]();
26324     },
26325
26326     // private
26327     onClick : function(e){
26328         if(e){
26329             e.preventDefault();
26330         }
26331         if(e.button != 0){
26332             return;
26333         }
26334         if(!this.disabled){
26335             if(this.enableToggle){
26336                 this.toggle();
26337             }
26338             if(this.menu && !this.menu.isVisible()){
26339                 this.menu.show(this.el, this.menuAlign);
26340             }
26341             this.fireEvent("click", this, e);
26342             if(this.handler){
26343                 this.el.removeClass("x-btn-over");
26344                 this.handler.call(this.scope || this, this, e);
26345             }
26346         }
26347     },
26348     // private
26349     onMouseOver : function(e){
26350         if(!this.disabled){
26351             this.el.addClass("x-btn-over");
26352             this.fireEvent('mouseover', this, e);
26353         }
26354     },
26355     // private
26356     onMouseOut : function(e){
26357         if(!e.within(this.el,  true)){
26358             this.el.removeClass("x-btn-over");
26359             this.fireEvent('mouseout', this, e);
26360         }
26361     },
26362     // private
26363     onFocus : function(e){
26364         if(!this.disabled){
26365             this.el.addClass("x-btn-focus");
26366         }
26367     },
26368     // private
26369     onBlur : function(e){
26370         this.el.removeClass("x-btn-focus");
26371     },
26372     // private
26373     onMouseDown : function(e){
26374         if(!this.disabled && e.button == 0){
26375             this.el.addClass("x-btn-click");
26376             Roo.get(document).on('mouseup', this.onMouseUp, this);
26377         }
26378     },
26379     // private
26380     onMouseUp : function(e){
26381         if(e.button == 0){
26382             this.el.removeClass("x-btn-click");
26383             Roo.get(document).un('mouseup', this.onMouseUp, this);
26384         }
26385     },
26386     // private
26387     onMenuShow : function(e){
26388         this.el.addClass("x-btn-menu-active");
26389     },
26390     // private
26391     onMenuHide : function(e){
26392         this.el.removeClass("x-btn-menu-active");
26393     }   
26394 });
26395
26396 // Private utility class used by Button
26397 Roo.ButtonToggleMgr = function(){
26398    var groups = {};
26399    
26400    function toggleGroup(btn, state){
26401        if(state){
26402            var g = groups[btn.toggleGroup];
26403            for(var i = 0, l = g.length; i < l; i++){
26404                if(g[i] != btn){
26405                    g[i].toggle(false);
26406                }
26407            }
26408        }
26409    }
26410    
26411    return {
26412        register : function(btn){
26413            if(!btn.toggleGroup){
26414                return;
26415            }
26416            var g = groups[btn.toggleGroup];
26417            if(!g){
26418                g = groups[btn.toggleGroup] = [];
26419            }
26420            g.push(btn);
26421            btn.on("toggle", toggleGroup);
26422        },
26423        
26424        unregister : function(btn){
26425            if(!btn.toggleGroup){
26426                return;
26427            }
26428            var g = groups[btn.toggleGroup];
26429            if(g){
26430                g.remove(btn);
26431                btn.un("toggle", toggleGroup);
26432            }
26433        }
26434    };
26435 }();/*
26436  * Based on:
26437  * Ext JS Library 1.1.1
26438  * Copyright(c) 2006-2007, Ext JS, LLC.
26439  *
26440  * Originally Released Under LGPL - original licence link has changed is not relivant.
26441  *
26442  * Fork - LGPL
26443  * <script type="text/javascript">
26444  */
26445  
26446 /**
26447  * @class Roo.SplitButton
26448  * @extends Roo.Button
26449  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26450  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26451  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26452  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26453  * @cfg {String} arrowTooltip The title attribute of the arrow
26454  * @constructor
26455  * Create a new menu button
26456  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26457  * @param {Object} config The config object
26458  */
26459 Roo.SplitButton = function(renderTo, config){
26460     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26461     /**
26462      * @event arrowclick
26463      * Fires when this button's arrow is clicked
26464      * @param {SplitButton} this
26465      * @param {EventObject} e The click event
26466      */
26467     this.addEvents({"arrowclick":true});
26468 };
26469
26470 Roo.extend(Roo.SplitButton, Roo.Button, {
26471     render : function(renderTo){
26472         // this is one sweet looking template!
26473         var tpl = new Roo.Template(
26474             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26475             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26476             '<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>',
26477             "</tbody></table></td><td>",
26478             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26479             '<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>',
26480             "</tbody></table></td></tr></table>"
26481         );
26482         var btn = tpl.append(renderTo, [this.text, this.type], true);
26483         var btnEl = btn.child("button");
26484         if(this.cls){
26485             btn.addClass(this.cls);
26486         }
26487         if(this.icon){
26488             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26489         }
26490         if(this.iconCls){
26491             btnEl.addClass(this.iconCls);
26492             if(!this.cls){
26493                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26494             }
26495         }
26496         this.el = btn;
26497         if(this.handleMouseEvents){
26498             btn.on("mouseover", this.onMouseOver, this);
26499             btn.on("mouseout", this.onMouseOut, this);
26500             btn.on("mousedown", this.onMouseDown, this);
26501             btn.on("mouseup", this.onMouseUp, this);
26502         }
26503         btn.on(this.clickEvent, this.onClick, this);
26504         if(this.tooltip){
26505             if(typeof this.tooltip == 'object'){
26506                 Roo.QuickTips.tips(Roo.apply({
26507                       target: btnEl.id
26508                 }, this.tooltip));
26509             } else {
26510                 btnEl.dom[this.tooltipType] = this.tooltip;
26511             }
26512         }
26513         if(this.arrowTooltip){
26514             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26515         }
26516         if(this.hidden){
26517             this.hide();
26518         }
26519         if(this.disabled){
26520             this.disable();
26521         }
26522         if(this.pressed){
26523             this.el.addClass("x-btn-pressed");
26524         }
26525         if(Roo.isIE && !Roo.isIE7){
26526             this.autoWidth.defer(1, this);
26527         }else{
26528             this.autoWidth();
26529         }
26530         if(this.menu){
26531             this.menu.on("show", this.onMenuShow, this);
26532             this.menu.on("hide", this.onMenuHide, this);
26533         }
26534         this.fireEvent('render', this);
26535     },
26536
26537     // private
26538     autoWidth : function(){
26539         if(this.el){
26540             var tbl = this.el.child("table:first");
26541             var tbl2 = this.el.child("table:last");
26542             this.el.setWidth("auto");
26543             tbl.setWidth("auto");
26544             if(Roo.isIE7 && Roo.isStrict){
26545                 var ib = this.el.child('button:first');
26546                 if(ib && ib.getWidth() > 20){
26547                     ib.clip();
26548                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26549                 }
26550             }
26551             if(this.minWidth){
26552                 if(this.hidden){
26553                     this.el.beginMeasure();
26554                 }
26555                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26556                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26557                 }
26558                 if(this.hidden){
26559                     this.el.endMeasure();
26560                 }
26561             }
26562             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26563         } 
26564     },
26565     /**
26566      * Sets this button's click handler
26567      * @param {Function} handler The function to call when the button is clicked
26568      * @param {Object} scope (optional) Scope for the function passed above
26569      */
26570     setHandler : function(handler, scope){
26571         this.handler = handler;
26572         this.scope = scope;  
26573     },
26574     
26575     /**
26576      * Sets this button's arrow click handler
26577      * @param {Function} handler The function to call when the arrow is clicked
26578      * @param {Object} scope (optional) Scope for the function passed above
26579      */
26580     setArrowHandler : function(handler, scope){
26581         this.arrowHandler = handler;
26582         this.scope = scope;  
26583     },
26584     
26585     /**
26586      * Focus the button
26587      */
26588     focus : function(){
26589         if(this.el){
26590             this.el.child("button:first").focus();
26591         }
26592     },
26593
26594     // private
26595     onClick : function(e){
26596         e.preventDefault();
26597         if(!this.disabled){
26598             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26599                 if(this.menu && !this.menu.isVisible()){
26600                     this.menu.show(this.el, this.menuAlign);
26601                 }
26602                 this.fireEvent("arrowclick", this, e);
26603                 if(this.arrowHandler){
26604                     this.arrowHandler.call(this.scope || this, this, e);
26605                 }
26606             }else{
26607                 this.fireEvent("click", this, e);
26608                 if(this.handler){
26609                     this.handler.call(this.scope || this, this, e);
26610                 }
26611             }
26612         }
26613     },
26614     // private
26615     onMouseDown : function(e){
26616         if(!this.disabled){
26617             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26618         }
26619     },
26620     // private
26621     onMouseUp : function(e){
26622         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26623     }   
26624 });
26625
26626
26627 // backwards compat
26628 Roo.MenuButton = Roo.SplitButton;/*
26629  * Based on:
26630  * Ext JS Library 1.1.1
26631  * Copyright(c) 2006-2007, Ext JS, LLC.
26632  *
26633  * Originally Released Under LGPL - original licence link has changed is not relivant.
26634  *
26635  * Fork - LGPL
26636  * <script type="text/javascript">
26637  */
26638
26639 /**
26640  * @class Roo.Toolbar
26641  * Basic Toolbar class.
26642  * @constructor
26643  * Creates a new Toolbar
26644  * @param {Object} config The config object
26645  */ 
26646 Roo.Toolbar = function(container, buttons, config)
26647 {
26648     /// old consturctor format still supported..
26649     if(container instanceof Array){ // omit the container for later rendering
26650         buttons = container;
26651         config = buttons;
26652         container = null;
26653     }
26654     if (typeof(container) == 'object' && container.xtype) {
26655         config = container;
26656         container = config.container;
26657         buttons = config.buttons; // not really - use items!!
26658     }
26659     var xitems = [];
26660     if (config && config.items) {
26661         xitems = config.items;
26662         delete config.items;
26663     }
26664     Roo.apply(this, config);
26665     this.buttons = buttons;
26666     
26667     if(container){
26668         this.render(container);
26669     }
26670     Roo.each(xitems, function(b) {
26671         this.add(b);
26672     }, this);
26673     
26674 };
26675
26676 Roo.Toolbar.prototype = {
26677     /**
26678      * @cfg {Roo.data.Store} items
26679      * array of button configs or elements to add
26680      */
26681     
26682     /**
26683      * @cfg {String/HTMLElement/Element} container
26684      * The id or element that will contain the toolbar
26685      */
26686     // private
26687     render : function(ct){
26688         this.el = Roo.get(ct);
26689         if(this.cls){
26690             this.el.addClass(this.cls);
26691         }
26692         // using a table allows for vertical alignment
26693         // 100% width is needed by Safari...
26694         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26695         this.tr = this.el.child("tr", true);
26696         var autoId = 0;
26697         this.items = new Roo.util.MixedCollection(false, function(o){
26698             return o.id || ("item" + (++autoId));
26699         });
26700         if(this.buttons){
26701             this.add.apply(this, this.buttons);
26702             delete this.buttons;
26703         }
26704     },
26705
26706     /**
26707      * Adds element(s) to the toolbar -- this function takes a variable number of 
26708      * arguments of mixed type and adds them to the toolbar.
26709      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26710      * <ul>
26711      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26712      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26713      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26714      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26715      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26716      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26717      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26718      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26719      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26720      * </ul>
26721      * @param {Mixed} arg2
26722      * @param {Mixed} etc.
26723      */
26724     add : function(){
26725         var a = arguments, l = a.length;
26726         for(var i = 0; i < l; i++){
26727             this._add(a[i]);
26728         }
26729     },
26730     // private..
26731     _add : function(el) {
26732         
26733         if (el.xtype) {
26734             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26735         }
26736         
26737         if (el.applyTo){ // some kind of form field
26738             return this.addField(el);
26739         } 
26740         if (el.render){ // some kind of Toolbar.Item
26741             return this.addItem(el);
26742         }
26743         if (typeof el == "string"){ // string
26744             if(el == "separator" || el == "-"){
26745                 return this.addSeparator();
26746             }
26747             if (el == " "){
26748                 return this.addSpacer();
26749             }
26750             if(el == "->"){
26751                 return this.addFill();
26752             }
26753             return this.addText(el);
26754             
26755         }
26756         if(el.tagName){ // element
26757             return this.addElement(el);
26758         }
26759         if(typeof el == "object"){ // must be button config?
26760             return this.addButton(el);
26761         }
26762         // and now what?!?!
26763         return false;
26764         
26765     },
26766     
26767     /**
26768      * Add an Xtype element
26769      * @param {Object} xtype Xtype Object
26770      * @return {Object} created Object
26771      */
26772     addxtype : function(e){
26773         return this.add(e);  
26774     },
26775     
26776     /**
26777      * Returns the Element for this toolbar.
26778      * @return {Roo.Element}
26779      */
26780     getEl : function(){
26781         return this.el;  
26782     },
26783     
26784     /**
26785      * Adds a separator
26786      * @return {Roo.Toolbar.Item} The separator item
26787      */
26788     addSeparator : function(){
26789         return this.addItem(new Roo.Toolbar.Separator());
26790     },
26791
26792     /**
26793      * Adds a spacer element
26794      * @return {Roo.Toolbar.Spacer} The spacer item
26795      */
26796     addSpacer : function(){
26797         return this.addItem(new Roo.Toolbar.Spacer());
26798     },
26799
26800     /**
26801      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26802      * @return {Roo.Toolbar.Fill} The fill item
26803      */
26804     addFill : function(){
26805         return this.addItem(new Roo.Toolbar.Fill());
26806     },
26807
26808     /**
26809      * Adds any standard HTML element to the toolbar
26810      * @param {String/HTMLElement/Element} el The element or id of the element to add
26811      * @return {Roo.Toolbar.Item} The element's item
26812      */
26813     addElement : function(el){
26814         return this.addItem(new Roo.Toolbar.Item(el));
26815     },
26816     /**
26817      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26818      * @type Roo.util.MixedCollection  
26819      */
26820     items : false,
26821      
26822     /**
26823      * Adds any Toolbar.Item or subclass
26824      * @param {Roo.Toolbar.Item} item
26825      * @return {Roo.Toolbar.Item} The item
26826      */
26827     addItem : function(item){
26828         var td = this.nextBlock();
26829         item.render(td);
26830         this.items.add(item);
26831         return item;
26832     },
26833     
26834     /**
26835      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26836      * @param {Object/Array} config A button config or array of configs
26837      * @return {Roo.Toolbar.Button/Array}
26838      */
26839     addButton : function(config){
26840         if(config instanceof Array){
26841             var buttons = [];
26842             for(var i = 0, len = config.length; i < len; i++) {
26843                 buttons.push(this.addButton(config[i]));
26844             }
26845             return buttons;
26846         }
26847         var b = config;
26848         if(!(config instanceof Roo.Toolbar.Button)){
26849             b = config.split ?
26850                 new Roo.Toolbar.SplitButton(config) :
26851                 new Roo.Toolbar.Button(config);
26852         }
26853         var td = this.nextBlock();
26854         b.render(td);
26855         this.items.add(b);
26856         return b;
26857     },
26858     
26859     /**
26860      * Adds text to the toolbar
26861      * @param {String} text The text to add
26862      * @return {Roo.Toolbar.Item} The element's item
26863      */
26864     addText : function(text){
26865         return this.addItem(new Roo.Toolbar.TextItem(text));
26866     },
26867     
26868     /**
26869      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26870      * @param {Number} index The index where the item is to be inserted
26871      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26872      * @return {Roo.Toolbar.Button/Item}
26873      */
26874     insertButton : function(index, item){
26875         if(item instanceof Array){
26876             var buttons = [];
26877             for(var i = 0, len = item.length; i < len; i++) {
26878                buttons.push(this.insertButton(index + i, item[i]));
26879             }
26880             return buttons;
26881         }
26882         if (!(item instanceof Roo.Toolbar.Button)){
26883            item = new Roo.Toolbar.Button(item);
26884         }
26885         var td = document.createElement("td");
26886         this.tr.insertBefore(td, this.tr.childNodes[index]);
26887         item.render(td);
26888         this.items.insert(index, item);
26889         return item;
26890     },
26891     
26892     /**
26893      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26894      * @param {Object} config
26895      * @return {Roo.Toolbar.Item} The element's item
26896      */
26897     addDom : function(config, returnEl){
26898         var td = this.nextBlock();
26899         Roo.DomHelper.overwrite(td, config);
26900         var ti = new Roo.Toolbar.Item(td.firstChild);
26901         ti.render(td);
26902         this.items.add(ti);
26903         return ti;
26904     },
26905
26906     /**
26907      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26908      * @type Roo.util.MixedCollection  
26909      */
26910     fields : false,
26911     
26912     /**
26913      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26914      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26915      * @param {Roo.form.Field} field
26916      * @return {Roo.ToolbarItem}
26917      */
26918      
26919       
26920     addField : function(field) {
26921         if (!this.fields) {
26922             var autoId = 0;
26923             this.fields = new Roo.util.MixedCollection(false, function(o){
26924                 return o.id || ("item" + (++autoId));
26925             });
26926
26927         }
26928         
26929         var td = this.nextBlock();
26930         field.render(td);
26931         var ti = new Roo.Toolbar.Item(td.firstChild);
26932         ti.render(td);
26933         this.items.add(ti);
26934         this.fields.add(field);
26935         return ti;
26936     },
26937     /**
26938      * Hide the toolbar
26939      * @method hide
26940      */
26941      
26942       
26943     hide : function()
26944     {
26945         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26946         this.el.child('div').hide();
26947     },
26948     /**
26949      * Show the toolbar
26950      * @method show
26951      */
26952     show : function()
26953     {
26954         this.el.child('div').show();
26955     },
26956       
26957     // private
26958     nextBlock : function(){
26959         var td = document.createElement("td");
26960         this.tr.appendChild(td);
26961         return td;
26962     },
26963
26964     // private
26965     destroy : function(){
26966         if(this.items){ // rendered?
26967             Roo.destroy.apply(Roo, this.items.items);
26968         }
26969         if(this.fields){ // rendered?
26970             Roo.destroy.apply(Roo, this.fields.items);
26971         }
26972         Roo.Element.uncache(this.el, this.tr);
26973     }
26974 };
26975
26976 /**
26977  * @class Roo.Toolbar.Item
26978  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26979  * @constructor
26980  * Creates a new Item
26981  * @param {HTMLElement} el 
26982  */
26983 Roo.Toolbar.Item = function(el){
26984     this.el = Roo.getDom(el);
26985     this.id = Roo.id(this.el);
26986     this.hidden = false;
26987 };
26988
26989 Roo.Toolbar.Item.prototype = {
26990     
26991     /**
26992      * Get this item's HTML Element
26993      * @return {HTMLElement}
26994      */
26995     getEl : function(){
26996        return this.el;  
26997     },
26998
26999     // private
27000     render : function(td){
27001         this.td = td;
27002         td.appendChild(this.el);
27003     },
27004     
27005     /**
27006      * Removes and destroys this item.
27007      */
27008     destroy : function(){
27009         this.td.parentNode.removeChild(this.td);
27010     },
27011     
27012     /**
27013      * Shows this item.
27014      */
27015     show: function(){
27016         this.hidden = false;
27017         this.td.style.display = "";
27018     },
27019     
27020     /**
27021      * Hides this item.
27022      */
27023     hide: function(){
27024         this.hidden = true;
27025         this.td.style.display = "none";
27026     },
27027     
27028     /**
27029      * Convenience function for boolean show/hide.
27030      * @param {Boolean} visible true to show/false to hide
27031      */
27032     setVisible: function(visible){
27033         if(visible) {
27034             this.show();
27035         }else{
27036             this.hide();
27037         }
27038     },
27039     
27040     /**
27041      * Try to focus this item.
27042      */
27043     focus : function(){
27044         Roo.fly(this.el).focus();
27045     },
27046     
27047     /**
27048      * Disables this item.
27049      */
27050     disable : function(){
27051         Roo.fly(this.td).addClass("x-item-disabled");
27052         this.disabled = true;
27053         this.el.disabled = true;
27054     },
27055     
27056     /**
27057      * Enables this item.
27058      */
27059     enable : function(){
27060         Roo.fly(this.td).removeClass("x-item-disabled");
27061         this.disabled = false;
27062         this.el.disabled = false;
27063     }
27064 };
27065
27066
27067 /**
27068  * @class Roo.Toolbar.Separator
27069  * @extends Roo.Toolbar.Item
27070  * A simple toolbar separator class
27071  * @constructor
27072  * Creates a new Separator
27073  */
27074 Roo.Toolbar.Separator = function(){
27075     var s = document.createElement("span");
27076     s.className = "ytb-sep";
27077     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27078 };
27079 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27080     enable:Roo.emptyFn,
27081     disable:Roo.emptyFn,
27082     focus:Roo.emptyFn
27083 });
27084
27085 /**
27086  * @class Roo.Toolbar.Spacer
27087  * @extends Roo.Toolbar.Item
27088  * A simple element that adds extra horizontal space to a toolbar.
27089  * @constructor
27090  * Creates a new Spacer
27091  */
27092 Roo.Toolbar.Spacer = function(){
27093     var s = document.createElement("div");
27094     s.className = "ytb-spacer";
27095     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27096 };
27097 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27098     enable:Roo.emptyFn,
27099     disable:Roo.emptyFn,
27100     focus:Roo.emptyFn
27101 });
27102
27103 /**
27104  * @class Roo.Toolbar.Fill
27105  * @extends Roo.Toolbar.Spacer
27106  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27107  * @constructor
27108  * Creates a new Spacer
27109  */
27110 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27111     // private
27112     render : function(td){
27113         td.style.width = '100%';
27114         Roo.Toolbar.Fill.superclass.render.call(this, td);
27115     }
27116 });
27117
27118 /**
27119  * @class Roo.Toolbar.TextItem
27120  * @extends Roo.Toolbar.Item
27121  * A simple class that renders text directly into a toolbar.
27122  * @constructor
27123  * Creates a new TextItem
27124  * @param {String} text
27125  */
27126 Roo.Toolbar.TextItem = function(text){
27127     if (typeof(text) == 'object') {
27128         text = text.text;
27129     }
27130     var s = document.createElement("span");
27131     s.className = "ytb-text";
27132     s.innerHTML = text;
27133     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27134 };
27135 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27136     enable:Roo.emptyFn,
27137     disable:Roo.emptyFn,
27138     focus:Roo.emptyFn
27139 });
27140
27141 /**
27142  * @class Roo.Toolbar.Button
27143  * @extends Roo.Button
27144  * A button that renders into a toolbar.
27145  * @constructor
27146  * Creates a new Button
27147  * @param {Object} config A standard {@link Roo.Button} config object
27148  */
27149 Roo.Toolbar.Button = function(config){
27150     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27151 };
27152 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27153     render : function(td){
27154         this.td = td;
27155         Roo.Toolbar.Button.superclass.render.call(this, td);
27156     },
27157     
27158     /**
27159      * Removes and destroys this button
27160      */
27161     destroy : function(){
27162         Roo.Toolbar.Button.superclass.destroy.call(this);
27163         this.td.parentNode.removeChild(this.td);
27164     },
27165     
27166     /**
27167      * Shows this button
27168      */
27169     show: function(){
27170         this.hidden = false;
27171         this.td.style.display = "";
27172     },
27173     
27174     /**
27175      * Hides this button
27176      */
27177     hide: function(){
27178         this.hidden = true;
27179         this.td.style.display = "none";
27180     },
27181
27182     /**
27183      * Disables this item
27184      */
27185     disable : function(){
27186         Roo.fly(this.td).addClass("x-item-disabled");
27187         this.disabled = true;
27188     },
27189
27190     /**
27191      * Enables this item
27192      */
27193     enable : function(){
27194         Roo.fly(this.td).removeClass("x-item-disabled");
27195         this.disabled = false;
27196     }
27197 });
27198 // backwards compat
27199 Roo.ToolbarButton = Roo.Toolbar.Button;
27200
27201 /**
27202  * @class Roo.Toolbar.SplitButton
27203  * @extends Roo.SplitButton
27204  * A menu button that renders into a toolbar.
27205  * @constructor
27206  * Creates a new SplitButton
27207  * @param {Object} config A standard {@link Roo.SplitButton} config object
27208  */
27209 Roo.Toolbar.SplitButton = function(config){
27210     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27211 };
27212 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27213     render : function(td){
27214         this.td = td;
27215         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27216     },
27217     
27218     /**
27219      * Removes and destroys this button
27220      */
27221     destroy : function(){
27222         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27223         this.td.parentNode.removeChild(this.td);
27224     },
27225     
27226     /**
27227      * Shows this button
27228      */
27229     show: function(){
27230         this.hidden = false;
27231         this.td.style.display = "";
27232     },
27233     
27234     /**
27235      * Hides this button
27236      */
27237     hide: function(){
27238         this.hidden = true;
27239         this.td.style.display = "none";
27240     }
27241 });
27242
27243 // backwards compat
27244 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27245  * Based on:
27246  * Ext JS Library 1.1.1
27247  * Copyright(c) 2006-2007, Ext JS, LLC.
27248  *
27249  * Originally Released Under LGPL - original licence link has changed is not relivant.
27250  *
27251  * Fork - LGPL
27252  * <script type="text/javascript">
27253  */
27254  
27255 /**
27256  * @class Roo.PagingToolbar
27257  * @extends Roo.Toolbar
27258  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27259  * @constructor
27260  * Create a new PagingToolbar
27261  * @param {Object} config The config object
27262  */
27263 Roo.PagingToolbar = function(el, ds, config)
27264 {
27265     // old args format still supported... - xtype is prefered..
27266     if (typeof(el) == 'object' && el.xtype) {
27267         // created from xtype...
27268         config = el;
27269         ds = el.dataSource;
27270         el = config.container;
27271     }
27272     var items = [];
27273     if (config.items) {
27274         items = config.items;
27275         config.items = [];
27276     }
27277     
27278     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27279     this.ds = ds;
27280     this.cursor = 0;
27281     this.renderButtons(this.el);
27282     this.bind(ds);
27283     
27284     // supprot items array.
27285    
27286     Roo.each(items, function(e) {
27287         this.add(Roo.factory(e));
27288     },this);
27289     
27290 };
27291
27292 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27293     /**
27294      * @cfg {Roo.data.Store} dataSource
27295      * The underlying data store providing the paged data
27296      */
27297     /**
27298      * @cfg {String/HTMLElement/Element} container
27299      * container The id or element that will contain the toolbar
27300      */
27301     /**
27302      * @cfg {Boolean} displayInfo
27303      * True to display the displayMsg (defaults to false)
27304      */
27305     /**
27306      * @cfg {Number} pageSize
27307      * The number of records to display per page (defaults to 20)
27308      */
27309     pageSize: 20,
27310     /**
27311      * @cfg {String} displayMsg
27312      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27313      */
27314     displayMsg : 'Displaying {0} - {1} of {2}',
27315     /**
27316      * @cfg {String} emptyMsg
27317      * The message to display when no records are found (defaults to "No data to display")
27318      */
27319     emptyMsg : 'No data to display',
27320     /**
27321      * Customizable piece of the default paging text (defaults to "Page")
27322      * @type String
27323      */
27324     beforePageText : "Page",
27325     /**
27326      * Customizable piece of the default paging text (defaults to "of %0")
27327      * @type String
27328      */
27329     afterPageText : "of {0}",
27330     /**
27331      * Customizable piece of the default paging text (defaults to "First Page")
27332      * @type String
27333      */
27334     firstText : "First Page",
27335     /**
27336      * Customizable piece of the default paging text (defaults to "Previous Page")
27337      * @type String
27338      */
27339     prevText : "Previous Page",
27340     /**
27341      * Customizable piece of the default paging text (defaults to "Next Page")
27342      * @type String
27343      */
27344     nextText : "Next Page",
27345     /**
27346      * Customizable piece of the default paging text (defaults to "Last Page")
27347      * @type String
27348      */
27349     lastText : "Last Page",
27350     /**
27351      * Customizable piece of the default paging text (defaults to "Refresh")
27352      * @type String
27353      */
27354     refreshText : "Refresh",
27355
27356     // private
27357     renderButtons : function(el){
27358         Roo.PagingToolbar.superclass.render.call(this, el);
27359         this.first = this.addButton({
27360             tooltip: this.firstText,
27361             cls: "x-btn-icon x-grid-page-first",
27362             disabled: true,
27363             handler: this.onClick.createDelegate(this, ["first"])
27364         });
27365         this.prev = this.addButton({
27366             tooltip: this.prevText,
27367             cls: "x-btn-icon x-grid-page-prev",
27368             disabled: true,
27369             handler: this.onClick.createDelegate(this, ["prev"])
27370         });
27371         //this.addSeparator();
27372         this.add(this.beforePageText);
27373         this.field = Roo.get(this.addDom({
27374            tag: "input",
27375            type: "text",
27376            size: "3",
27377            value: "1",
27378            cls: "x-grid-page-number"
27379         }).el);
27380         this.field.on("keydown", this.onPagingKeydown, this);
27381         this.field.on("focus", function(){this.dom.select();});
27382         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27383         this.field.setHeight(18);
27384         //this.addSeparator();
27385         this.next = this.addButton({
27386             tooltip: this.nextText,
27387             cls: "x-btn-icon x-grid-page-next",
27388             disabled: true,
27389             handler: this.onClick.createDelegate(this, ["next"])
27390         });
27391         this.last = this.addButton({
27392             tooltip: this.lastText,
27393             cls: "x-btn-icon x-grid-page-last",
27394             disabled: true,
27395             handler: this.onClick.createDelegate(this, ["last"])
27396         });
27397         //this.addSeparator();
27398         this.loading = this.addButton({
27399             tooltip: this.refreshText,
27400             cls: "x-btn-icon x-grid-loading",
27401             handler: this.onClick.createDelegate(this, ["refresh"])
27402         });
27403
27404         if(this.displayInfo){
27405             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27406         }
27407     },
27408
27409     // private
27410     updateInfo : function(){
27411         if(this.displayEl){
27412             var count = this.ds.getCount();
27413             var msg = count == 0 ?
27414                 this.emptyMsg :
27415                 String.format(
27416                     this.displayMsg,
27417                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27418                 );
27419             this.displayEl.update(msg);
27420         }
27421     },
27422
27423     // private
27424     onLoad : function(ds, r, o){
27425        this.cursor = o.params ? o.params.start : 0;
27426        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27427
27428        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27429        this.field.dom.value = ap;
27430        this.first.setDisabled(ap == 1);
27431        this.prev.setDisabled(ap == 1);
27432        this.next.setDisabled(ap == ps);
27433        this.last.setDisabled(ap == ps);
27434        this.loading.enable();
27435        this.updateInfo();
27436     },
27437
27438     // private
27439     getPageData : function(){
27440         var total = this.ds.getTotalCount();
27441         return {
27442             total : total,
27443             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27444             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27445         };
27446     },
27447
27448     // private
27449     onLoadError : function(){
27450         this.loading.enable();
27451     },
27452
27453     // private
27454     onPagingKeydown : function(e){
27455         var k = e.getKey();
27456         var d = this.getPageData();
27457         if(k == e.RETURN){
27458             var v = this.field.dom.value, pageNum;
27459             if(!v || isNaN(pageNum = parseInt(v, 10))){
27460                 this.field.dom.value = d.activePage;
27461                 return;
27462             }
27463             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27464             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27465             e.stopEvent();
27466         }
27467         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))
27468         {
27469           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27470           this.field.dom.value = pageNum;
27471           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27472           e.stopEvent();
27473         }
27474         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27475         {
27476           var v = this.field.dom.value, pageNum; 
27477           var increment = (e.shiftKey) ? 10 : 1;
27478           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27479             increment *= -1;
27480           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27481             this.field.dom.value = d.activePage;
27482             return;
27483           }
27484           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27485           {
27486             this.field.dom.value = parseInt(v, 10) + increment;
27487             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27488             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27489           }
27490           e.stopEvent();
27491         }
27492     },
27493
27494     // private
27495     beforeLoad : function(){
27496         if(this.loading){
27497             this.loading.disable();
27498         }
27499     },
27500
27501     // private
27502     onClick : function(which){
27503         var ds = this.ds;
27504         switch(which){
27505             case "first":
27506                 ds.load({params:{start: 0, limit: this.pageSize}});
27507             break;
27508             case "prev":
27509                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27510             break;
27511             case "next":
27512                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27513             break;
27514             case "last":
27515                 var total = ds.getTotalCount();
27516                 var extra = total % this.pageSize;
27517                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27518                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27519             break;
27520             case "refresh":
27521                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27522             break;
27523         }
27524     },
27525
27526     /**
27527      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27528      * @param {Roo.data.Store} store The data store to unbind
27529      */
27530     unbind : function(ds){
27531         ds.un("beforeload", this.beforeLoad, this);
27532         ds.un("load", this.onLoad, this);
27533         ds.un("loadexception", this.onLoadError, this);
27534         ds.un("remove", this.updateInfo, this);
27535         ds.un("add", this.updateInfo, this);
27536         this.ds = undefined;
27537     },
27538
27539     /**
27540      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27541      * @param {Roo.data.Store} store The data store to bind
27542      */
27543     bind : function(ds){
27544         ds.on("beforeload", this.beforeLoad, this);
27545         ds.on("load", this.onLoad, this);
27546         ds.on("loadexception", this.onLoadError, this);
27547         ds.on("remove", this.updateInfo, this);
27548         ds.on("add", this.updateInfo, this);
27549         this.ds = ds;
27550     }
27551 });/*
27552  * Based on:
27553  * Ext JS Library 1.1.1
27554  * Copyright(c) 2006-2007, Ext JS, LLC.
27555  *
27556  * Originally Released Under LGPL - original licence link has changed is not relivant.
27557  *
27558  * Fork - LGPL
27559  * <script type="text/javascript">
27560  */
27561
27562 /**
27563  * @class Roo.Resizable
27564  * @extends Roo.util.Observable
27565  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27566  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27567  * 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
27568  * the element will be wrapped for you automatically.</p>
27569  * <p>Here is the list of valid resize handles:</p>
27570  * <pre>
27571 Value   Description
27572 ------  -------------------
27573  'n'     north
27574  's'     south
27575  'e'     east
27576  'w'     west
27577  'nw'    northwest
27578  'sw'    southwest
27579  'se'    southeast
27580  'ne'    northeast
27581  'hd'    horizontal drag
27582  'all'   all
27583 </pre>
27584  * <p>Here's an example showing the creation of a typical Resizable:</p>
27585  * <pre><code>
27586 var resizer = new Roo.Resizable("element-id", {
27587     handles: 'all',
27588     minWidth: 200,
27589     minHeight: 100,
27590     maxWidth: 500,
27591     maxHeight: 400,
27592     pinned: true
27593 });
27594 resizer.on("resize", myHandler);
27595 </code></pre>
27596  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27597  * resizer.east.setDisplayed(false);</p>
27598  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27599  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27600  * resize operation's new size (defaults to [0, 0])
27601  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27602  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27603  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27604  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27605  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27606  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27607  * @cfg {Number} width The width of the element in pixels (defaults to null)
27608  * @cfg {Number} height The height of the element in pixels (defaults to null)
27609  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27610  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27611  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27612  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27613  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27614  * in favor of the handles config option (defaults to false)
27615  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27616  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27617  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27618  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27619  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27620  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27621  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27622  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27623  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27624  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27625  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27626  * @constructor
27627  * Create a new resizable component
27628  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27629  * @param {Object} config configuration options
27630   */
27631 Roo.Resizable = function(el, config)
27632 {
27633     this.el = Roo.get(el);
27634
27635     if(config && config.wrap){
27636         config.resizeChild = this.el;
27637         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27638         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27639         this.el.setStyle("overflow", "hidden");
27640         this.el.setPositioning(config.resizeChild.getPositioning());
27641         config.resizeChild.clearPositioning();
27642         if(!config.width || !config.height){
27643             var csize = config.resizeChild.getSize();
27644             this.el.setSize(csize.width, csize.height);
27645         }
27646         if(config.pinned && !config.adjustments){
27647             config.adjustments = "auto";
27648         }
27649     }
27650
27651     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27652     this.proxy.unselectable();
27653     this.proxy.enableDisplayMode('block');
27654
27655     Roo.apply(this, config);
27656
27657     if(this.pinned){
27658         this.disableTrackOver = true;
27659         this.el.addClass("x-resizable-pinned");
27660     }
27661     // if the element isn't positioned, make it relative
27662     var position = this.el.getStyle("position");
27663     if(position != "absolute" && position != "fixed"){
27664         this.el.setStyle("position", "relative");
27665     }
27666     if(!this.handles){ // no handles passed, must be legacy style
27667         this.handles = 's,e,se';
27668         if(this.multiDirectional){
27669             this.handles += ',n,w';
27670         }
27671     }
27672     if(this.handles == "all"){
27673         this.handles = "n s e w ne nw se sw";
27674     }
27675     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27676     var ps = Roo.Resizable.positions;
27677     for(var i = 0, len = hs.length; i < len; i++){
27678         if(hs[i] && ps[hs[i]]){
27679             var pos = ps[hs[i]];
27680             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27681         }
27682     }
27683     // legacy
27684     this.corner = this.southeast;
27685     
27686     // updateBox = the box can move..
27687     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27688         this.updateBox = true;
27689     }
27690
27691     this.activeHandle = null;
27692
27693     if(this.resizeChild){
27694         if(typeof this.resizeChild == "boolean"){
27695             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27696         }else{
27697             this.resizeChild = Roo.get(this.resizeChild, true);
27698         }
27699     }
27700     
27701     if(this.adjustments == "auto"){
27702         var rc = this.resizeChild;
27703         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27704         if(rc && (hw || hn)){
27705             rc.position("relative");
27706             rc.setLeft(hw ? hw.el.getWidth() : 0);
27707             rc.setTop(hn ? hn.el.getHeight() : 0);
27708         }
27709         this.adjustments = [
27710             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27711             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27712         ];
27713     }
27714
27715     if(this.draggable){
27716         this.dd = this.dynamic ?
27717             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27718         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27719     }
27720
27721     // public events
27722     this.addEvents({
27723         /**
27724          * @event beforeresize
27725          * Fired before resize is allowed. Set enabled to false to cancel resize.
27726          * @param {Roo.Resizable} this
27727          * @param {Roo.EventObject} e The mousedown event
27728          */
27729         "beforeresize" : true,
27730         /**
27731          * @event resize
27732          * Fired after a resize.
27733          * @param {Roo.Resizable} this
27734          * @param {Number} width The new width
27735          * @param {Number} height The new height
27736          * @param {Roo.EventObject} e The mouseup event
27737          */
27738         "resize" : true
27739     });
27740
27741     if(this.width !== null && this.height !== null){
27742         this.resizeTo(this.width, this.height);
27743     }else{
27744         this.updateChildSize();
27745     }
27746     if(Roo.isIE){
27747         this.el.dom.style.zoom = 1;
27748     }
27749     Roo.Resizable.superclass.constructor.call(this);
27750 };
27751
27752 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27753         resizeChild : false,
27754         adjustments : [0, 0],
27755         minWidth : 5,
27756         minHeight : 5,
27757         maxWidth : 10000,
27758         maxHeight : 10000,
27759         enabled : true,
27760         animate : false,
27761         duration : .35,
27762         dynamic : false,
27763         handles : false,
27764         multiDirectional : false,
27765         disableTrackOver : false,
27766         easing : 'easeOutStrong',
27767         widthIncrement : 0,
27768         heightIncrement : 0,
27769         pinned : false,
27770         width : null,
27771         height : null,
27772         preserveRatio : false,
27773         transparent: false,
27774         minX: 0,
27775         minY: 0,
27776         draggable: false,
27777
27778         /**
27779          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27780          */
27781         constrainTo: undefined,
27782         /**
27783          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27784          */
27785         resizeRegion: undefined,
27786
27787
27788     /**
27789      * Perform a manual resize
27790      * @param {Number} width
27791      * @param {Number} height
27792      */
27793     resizeTo : function(width, height){
27794         this.el.setSize(width, height);
27795         this.updateChildSize();
27796         this.fireEvent("resize", this, width, height, null);
27797     },
27798
27799     // private
27800     startSizing : function(e, handle){
27801         this.fireEvent("beforeresize", this, e);
27802         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27803
27804             if(!this.overlay){
27805                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27806                 this.overlay.unselectable();
27807                 this.overlay.enableDisplayMode("block");
27808                 this.overlay.on("mousemove", this.onMouseMove, this);
27809                 this.overlay.on("mouseup", this.onMouseUp, this);
27810             }
27811             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27812
27813             this.resizing = true;
27814             this.startBox = this.el.getBox();
27815             this.startPoint = e.getXY();
27816             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27817                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27818
27819             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27820             this.overlay.show();
27821
27822             if(this.constrainTo) {
27823                 var ct = Roo.get(this.constrainTo);
27824                 this.resizeRegion = ct.getRegion().adjust(
27825                     ct.getFrameWidth('t'),
27826                     ct.getFrameWidth('l'),
27827                     -ct.getFrameWidth('b'),
27828                     -ct.getFrameWidth('r')
27829                 );
27830             }
27831
27832             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27833             this.proxy.show();
27834             this.proxy.setBox(this.startBox);
27835             if(!this.dynamic){
27836                 this.proxy.setStyle('visibility', 'visible');
27837             }
27838         }
27839     },
27840
27841     // private
27842     onMouseDown : function(handle, e){
27843         if(this.enabled){
27844             e.stopEvent();
27845             this.activeHandle = handle;
27846             this.startSizing(e, handle);
27847         }
27848     },
27849
27850     // private
27851     onMouseUp : function(e){
27852         var size = this.resizeElement();
27853         this.resizing = false;
27854         this.handleOut();
27855         this.overlay.hide();
27856         this.proxy.hide();
27857         this.fireEvent("resize", this, size.width, size.height, e);
27858     },
27859
27860     // private
27861     updateChildSize : function(){
27862         if(this.resizeChild){
27863             var el = this.el;
27864             var child = this.resizeChild;
27865             var adj = this.adjustments;
27866             if(el.dom.offsetWidth){
27867                 var b = el.getSize(true);
27868                 child.setSize(b.width+adj[0], b.height+adj[1]);
27869             }
27870             // Second call here for IE
27871             // The first call enables instant resizing and
27872             // the second call corrects scroll bars if they
27873             // exist
27874             if(Roo.isIE){
27875                 setTimeout(function(){
27876                     if(el.dom.offsetWidth){
27877                         var b = el.getSize(true);
27878                         child.setSize(b.width+adj[0], b.height+adj[1]);
27879                     }
27880                 }, 10);
27881             }
27882         }
27883     },
27884
27885     // private
27886     snap : function(value, inc, min){
27887         if(!inc || !value) return value;
27888         var newValue = value;
27889         var m = value % inc;
27890         if(m > 0){
27891             if(m > (inc/2)){
27892                 newValue = value + (inc-m);
27893             }else{
27894                 newValue = value - m;
27895             }
27896         }
27897         return Math.max(min, newValue);
27898     },
27899
27900     // private
27901     resizeElement : function(){
27902         var box = this.proxy.getBox();
27903         if(this.updateBox){
27904             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27905         }else{
27906             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27907         }
27908         this.updateChildSize();
27909         if(!this.dynamic){
27910             this.proxy.hide();
27911         }
27912         return box;
27913     },
27914
27915     // private
27916     constrain : function(v, diff, m, mx){
27917         if(v - diff < m){
27918             diff = v - m;
27919         }else if(v - diff > mx){
27920             diff = mx - v;
27921         }
27922         return diff;
27923     },
27924
27925     // private
27926     onMouseMove : function(e){
27927         if(this.enabled){
27928             try{// try catch so if something goes wrong the user doesn't get hung
27929
27930             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27931                 return;
27932             }
27933
27934             //var curXY = this.startPoint;
27935             var curSize = this.curSize || this.startBox;
27936             var x = this.startBox.x, y = this.startBox.y;
27937             var ox = x, oy = y;
27938             var w = curSize.width, h = curSize.height;
27939             var ow = w, oh = h;
27940             var mw = this.minWidth, mh = this.minHeight;
27941             var mxw = this.maxWidth, mxh = this.maxHeight;
27942             var wi = this.widthIncrement;
27943             var hi = this.heightIncrement;
27944
27945             var eventXY = e.getXY();
27946             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27947             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27948
27949             var pos = this.activeHandle.position;
27950
27951             switch(pos){
27952                 case "east":
27953                     w += diffX;
27954                     w = Math.min(Math.max(mw, w), mxw);
27955                     break;
27956              
27957                 case "south":
27958                     h += diffY;
27959                     h = Math.min(Math.max(mh, h), mxh);
27960                     break;
27961                 case "southeast":
27962                     w += diffX;
27963                     h += diffY;
27964                     w = Math.min(Math.max(mw, w), mxw);
27965                     h = Math.min(Math.max(mh, h), mxh);
27966                     break;
27967                 case "north":
27968                     diffY = this.constrain(h, diffY, mh, mxh);
27969                     y += diffY;
27970                     h -= diffY;
27971                     break;
27972                 case "hdrag":
27973                     
27974                     if (wi) {
27975                         var adiffX = Math.abs(diffX);
27976                         var sub = (adiffX % wi); // how much 
27977                         if (sub > (wi/2)) { // far enough to snap
27978                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
27979                         } else {
27980                             // remove difference.. 
27981                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
27982                         }
27983                     }
27984                     x += diffX;
27985                     x = Math.max(this.minX, x);
27986                     break;
27987                 case "west":
27988                     diffX = this.constrain(w, diffX, mw, mxw);
27989                     x += diffX;
27990                     w -= diffX;
27991                     break;
27992                 case "northeast":
27993                     w += diffX;
27994                     w = Math.min(Math.max(mw, w), mxw);
27995                     diffY = this.constrain(h, diffY, mh, mxh);
27996                     y += diffY;
27997                     h -= diffY;
27998                     break;
27999                 case "northwest":
28000                     diffX = this.constrain(w, diffX, mw, mxw);
28001                     diffY = this.constrain(h, diffY, mh, mxh);
28002                     y += diffY;
28003                     h -= diffY;
28004                     x += diffX;
28005                     w -= diffX;
28006                     break;
28007                case "southwest":
28008                     diffX = this.constrain(w, diffX, mw, mxw);
28009                     h += diffY;
28010                     h = Math.min(Math.max(mh, h), mxh);
28011                     x += diffX;
28012                     w -= diffX;
28013                     break;
28014             }
28015
28016             var sw = this.snap(w, wi, mw);
28017             var sh = this.snap(h, hi, mh);
28018             if(sw != w || sh != h){
28019                 switch(pos){
28020                     case "northeast":
28021                         y -= sh - h;
28022                     break;
28023                     case "north":
28024                         y -= sh - h;
28025                         break;
28026                     case "southwest":
28027                         x -= sw - w;
28028                     break;
28029                     case "west":
28030                         x -= sw - w;
28031                         break;
28032                     case "northwest":
28033                         x -= sw - w;
28034                         y -= sh - h;
28035                     break;
28036                 }
28037                 w = sw;
28038                 h = sh;
28039             }
28040
28041             if(this.preserveRatio){
28042                 switch(pos){
28043                     case "southeast":
28044                     case "east":
28045                         h = oh * (w/ow);
28046                         h = Math.min(Math.max(mh, h), mxh);
28047                         w = ow * (h/oh);
28048                        break;
28049                     case "south":
28050                         w = ow * (h/oh);
28051                         w = Math.min(Math.max(mw, w), mxw);
28052                         h = oh * (w/ow);
28053                         break;
28054                     case "northeast":
28055                         w = ow * (h/oh);
28056                         w = Math.min(Math.max(mw, w), mxw);
28057                         h = oh * (w/ow);
28058                     break;
28059                     case "north":
28060                         var tw = w;
28061                         w = ow * (h/oh);
28062                         w = Math.min(Math.max(mw, w), mxw);
28063                         h = oh * (w/ow);
28064                         x += (tw - w) / 2;
28065                         break;
28066                     case "southwest":
28067                         h = oh * (w/ow);
28068                         h = Math.min(Math.max(mh, h), mxh);
28069                         var tw = w;
28070                         w = ow * (h/oh);
28071                         x += tw - w;
28072                         break;
28073                     case "west":
28074                         var th = h;
28075                         h = oh * (w/ow);
28076                         h = Math.min(Math.max(mh, h), mxh);
28077                         y += (th - h) / 2;
28078                         var tw = w;
28079                         w = ow * (h/oh);
28080                         x += tw - w;
28081                        break;
28082                     case "northwest":
28083                         var tw = w;
28084                         var th = h;
28085                         h = oh * (w/ow);
28086                         h = Math.min(Math.max(mh, h), mxh);
28087                         w = ow * (h/oh);
28088                         y += th - h;
28089                         x += tw - w;
28090                        break;
28091
28092                 }
28093             }
28094             if (pos == 'hdrag') {
28095                 w = ow;
28096             }
28097             this.proxy.setBounds(x, y, w, h);
28098             if(this.dynamic){
28099                 this.resizeElement();
28100             }
28101             }catch(e){}
28102         }
28103     },
28104
28105     // private
28106     handleOver : function(){
28107         if(this.enabled){
28108             this.el.addClass("x-resizable-over");
28109         }
28110     },
28111
28112     // private
28113     handleOut : function(){
28114         if(!this.resizing){
28115             this.el.removeClass("x-resizable-over");
28116         }
28117     },
28118
28119     /**
28120      * Returns the element this component is bound to.
28121      * @return {Roo.Element}
28122      */
28123     getEl : function(){
28124         return this.el;
28125     },
28126
28127     /**
28128      * Returns the resizeChild element (or null).
28129      * @return {Roo.Element}
28130      */
28131     getResizeChild : function(){
28132         return this.resizeChild;
28133     },
28134
28135     /**
28136      * Destroys this resizable. If the element was wrapped and
28137      * removeEl is not true then the element remains.
28138      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28139      */
28140     destroy : function(removeEl){
28141         this.proxy.remove();
28142         if(this.overlay){
28143             this.overlay.removeAllListeners();
28144             this.overlay.remove();
28145         }
28146         var ps = Roo.Resizable.positions;
28147         for(var k in ps){
28148             if(typeof ps[k] != "function" && this[ps[k]]){
28149                 var h = this[ps[k]];
28150                 h.el.removeAllListeners();
28151                 h.el.remove();
28152             }
28153         }
28154         if(removeEl){
28155             this.el.update("");
28156             this.el.remove();
28157         }
28158     }
28159 });
28160
28161 // private
28162 // hash to map config positions to true positions
28163 Roo.Resizable.positions = {
28164     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28165     hd: "hdrag"
28166 };
28167
28168 // private
28169 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28170     if(!this.tpl){
28171         // only initialize the template if resizable is used
28172         var tpl = Roo.DomHelper.createTemplate(
28173             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28174         );
28175         tpl.compile();
28176         Roo.Resizable.Handle.prototype.tpl = tpl;
28177     }
28178     this.position = pos;
28179     this.rz = rz;
28180     // show north drag fro topdra
28181     var handlepos = pos == 'hdrag' ? 'north' : pos;
28182     
28183     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28184     if (pos == 'hdrag') {
28185         this.el.setStyle('cursor', 'pointer');
28186     }
28187     this.el.unselectable();
28188     if(transparent){
28189         this.el.setOpacity(0);
28190     }
28191     this.el.on("mousedown", this.onMouseDown, this);
28192     if(!disableTrackOver){
28193         this.el.on("mouseover", this.onMouseOver, this);
28194         this.el.on("mouseout", this.onMouseOut, this);
28195     }
28196 };
28197
28198 // private
28199 Roo.Resizable.Handle.prototype = {
28200     afterResize : function(rz){
28201         // do nothing
28202     },
28203     // private
28204     onMouseDown : function(e){
28205         this.rz.onMouseDown(this, e);
28206     },
28207     // private
28208     onMouseOver : function(e){
28209         this.rz.handleOver(this, e);
28210     },
28211     // private
28212     onMouseOut : function(e){
28213         this.rz.handleOut(this, e);
28214     }
28215 };/*
28216  * Based on:
28217  * Ext JS Library 1.1.1
28218  * Copyright(c) 2006-2007, Ext JS, LLC.
28219  *
28220  * Originally Released Under LGPL - original licence link has changed is not relivant.
28221  *
28222  * Fork - LGPL
28223  * <script type="text/javascript">
28224  */
28225
28226 /**
28227  * @class Roo.Editor
28228  * @extends Roo.Component
28229  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28230  * @constructor
28231  * Create a new Editor
28232  * @param {Roo.form.Field} field The Field object (or descendant)
28233  * @param {Object} config The config object
28234  */
28235 Roo.Editor = function(field, config){
28236     Roo.Editor.superclass.constructor.call(this, config);
28237     this.field = field;
28238     this.addEvents({
28239         /**
28240              * @event beforestartedit
28241              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28242              * false from the handler of this event.
28243              * @param {Editor} this
28244              * @param {Roo.Element} boundEl The underlying element bound to this editor
28245              * @param {Mixed} value The field value being set
28246              */
28247         "beforestartedit" : true,
28248         /**
28249              * @event startedit
28250              * Fires when this editor is displayed
28251              * @param {Roo.Element} boundEl The underlying element bound to this editor
28252              * @param {Mixed} value The starting field value
28253              */
28254         "startedit" : true,
28255         /**
28256              * @event beforecomplete
28257              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28258              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28259              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28260              * event will not fire since no edit actually occurred.
28261              * @param {Editor} this
28262              * @param {Mixed} value The current field value
28263              * @param {Mixed} startValue The original field value
28264              */
28265         "beforecomplete" : true,
28266         /**
28267              * @event complete
28268              * Fires after editing is complete and any changed value has been written to the underlying field.
28269              * @param {Editor} this
28270              * @param {Mixed} value The current field value
28271              * @param {Mixed} startValue The original field value
28272              */
28273         "complete" : true,
28274         /**
28275          * @event specialkey
28276          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28277          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28278          * @param {Roo.form.Field} this
28279          * @param {Roo.EventObject} e The event object
28280          */
28281         "specialkey" : true
28282     });
28283 };
28284
28285 Roo.extend(Roo.Editor, Roo.Component, {
28286     /**
28287      * @cfg {Boolean/String} autosize
28288      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28289      * or "height" to adopt the height only (defaults to false)
28290      */
28291     /**
28292      * @cfg {Boolean} revertInvalid
28293      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28294      * validation fails (defaults to true)
28295      */
28296     /**
28297      * @cfg {Boolean} ignoreNoChange
28298      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28299      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28300      * will never be ignored.
28301      */
28302     /**
28303      * @cfg {Boolean} hideEl
28304      * False to keep the bound element visible while the editor is displayed (defaults to true)
28305      */
28306     /**
28307      * @cfg {Mixed} value
28308      * The data value of the underlying field (defaults to "")
28309      */
28310     value : "",
28311     /**
28312      * @cfg {String} alignment
28313      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28314      */
28315     alignment: "c-c?",
28316     /**
28317      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28318      * for bottom-right shadow (defaults to "frame")
28319      */
28320     shadow : "frame",
28321     /**
28322      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28323      */
28324     constrain : false,
28325     /**
28326      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28327      */
28328     completeOnEnter : false,
28329     /**
28330      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28331      */
28332     cancelOnEsc : false,
28333     /**
28334      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28335      */
28336     updateEl : false,
28337
28338     // private
28339     onRender : function(ct, position){
28340         this.el = new Roo.Layer({
28341             shadow: this.shadow,
28342             cls: "x-editor",
28343             parentEl : ct,
28344             shim : this.shim,
28345             shadowOffset:4,
28346             id: this.id,
28347             constrain: this.constrain
28348         });
28349         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28350         if(this.field.msgTarget != 'title'){
28351             this.field.msgTarget = 'qtip';
28352         }
28353         this.field.render(this.el);
28354         if(Roo.isGecko){
28355             this.field.el.dom.setAttribute('autocomplete', 'off');
28356         }
28357         this.field.on("specialkey", this.onSpecialKey, this);
28358         if(this.swallowKeys){
28359             this.field.el.swallowEvent(['keydown','keypress']);
28360         }
28361         this.field.show();
28362         this.field.on("blur", this.onBlur, this);
28363         if(this.field.grow){
28364             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28365         }
28366     },
28367
28368     onSpecialKey : function(field, e)
28369     {
28370         //Roo.log('editor onSpecialKey');
28371         if(this.completeOnEnter && e.getKey() == e.ENTER){
28372             e.stopEvent();
28373             this.completeEdit();
28374             return;
28375         }
28376         // do not fire special key otherwise it might hide close the editor...
28377         if(e.getKey() == e.ENTER){    
28378             return;
28379         }
28380         if(this.cancelOnEsc && e.getKey() == e.ESC){
28381             this.cancelEdit();
28382             return;
28383         } 
28384         this.fireEvent('specialkey', field, e);
28385     
28386     },
28387
28388     /**
28389      * Starts the editing process and shows the editor.
28390      * @param {String/HTMLElement/Element} el The element to edit
28391      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28392       * to the innerHTML of el.
28393      */
28394     startEdit : function(el, value){
28395         if(this.editing){
28396             this.completeEdit();
28397         }
28398         this.boundEl = Roo.get(el);
28399         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28400         if(!this.rendered){
28401             this.render(this.parentEl || document.body);
28402         }
28403         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28404             return;
28405         }
28406         this.startValue = v;
28407         this.field.setValue(v);
28408         if(this.autoSize){
28409             var sz = this.boundEl.getSize();
28410             switch(this.autoSize){
28411                 case "width":
28412                 this.setSize(sz.width,  "");
28413                 break;
28414                 case "height":
28415                 this.setSize("",  sz.height);
28416                 break;
28417                 default:
28418                 this.setSize(sz.width,  sz.height);
28419             }
28420         }
28421         this.el.alignTo(this.boundEl, this.alignment);
28422         this.editing = true;
28423         if(Roo.QuickTips){
28424             Roo.QuickTips.disable();
28425         }
28426         this.show();
28427     },
28428
28429     /**
28430      * Sets the height and width of this editor.
28431      * @param {Number} width The new width
28432      * @param {Number} height The new height
28433      */
28434     setSize : function(w, h){
28435         this.field.setSize(w, h);
28436         if(this.el){
28437             this.el.sync();
28438         }
28439     },
28440
28441     /**
28442      * Realigns the editor to the bound field based on the current alignment config value.
28443      */
28444     realign : function(){
28445         this.el.alignTo(this.boundEl, this.alignment);
28446     },
28447
28448     /**
28449      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28450      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28451      */
28452     completeEdit : function(remainVisible){
28453         if(!this.editing){
28454             return;
28455         }
28456         var v = this.getValue();
28457         if(this.revertInvalid !== false && !this.field.isValid()){
28458             v = this.startValue;
28459             this.cancelEdit(true);
28460         }
28461         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28462             this.editing = false;
28463             this.hide();
28464             return;
28465         }
28466         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28467             this.editing = false;
28468             if(this.updateEl && this.boundEl){
28469                 this.boundEl.update(v);
28470             }
28471             if(remainVisible !== true){
28472                 this.hide();
28473             }
28474             this.fireEvent("complete", this, v, this.startValue);
28475         }
28476     },
28477
28478     // private
28479     onShow : function(){
28480         this.el.show();
28481         if(this.hideEl !== false){
28482             this.boundEl.hide();
28483         }
28484         this.field.show();
28485         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28486             this.fixIEFocus = true;
28487             this.deferredFocus.defer(50, this);
28488         }else{
28489             this.field.focus();
28490         }
28491         this.fireEvent("startedit", this.boundEl, this.startValue);
28492     },
28493
28494     deferredFocus : function(){
28495         if(this.editing){
28496             this.field.focus();
28497         }
28498     },
28499
28500     /**
28501      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28502      * reverted to the original starting value.
28503      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28504      * cancel (defaults to false)
28505      */
28506     cancelEdit : function(remainVisible){
28507         if(this.editing){
28508             this.setValue(this.startValue);
28509             if(remainVisible !== true){
28510                 this.hide();
28511             }
28512         }
28513     },
28514
28515     // private
28516     onBlur : function(){
28517         if(this.allowBlur !== true && this.editing){
28518             this.completeEdit();
28519         }
28520     },
28521
28522     // private
28523     onHide : function(){
28524         if(this.editing){
28525             this.completeEdit();
28526             return;
28527         }
28528         this.field.blur();
28529         if(this.field.collapse){
28530             this.field.collapse();
28531         }
28532         this.el.hide();
28533         if(this.hideEl !== false){
28534             this.boundEl.show();
28535         }
28536         if(Roo.QuickTips){
28537             Roo.QuickTips.enable();
28538         }
28539     },
28540
28541     /**
28542      * Sets the data value of the editor
28543      * @param {Mixed} value Any valid value supported by the underlying field
28544      */
28545     setValue : function(v){
28546         this.field.setValue(v);
28547     },
28548
28549     /**
28550      * Gets the data value of the editor
28551      * @return {Mixed} The data value
28552      */
28553     getValue : function(){
28554         return this.field.getValue();
28555     }
28556 });/*
28557  * Based on:
28558  * Ext JS Library 1.1.1
28559  * Copyright(c) 2006-2007, Ext JS, LLC.
28560  *
28561  * Originally Released Under LGPL - original licence link has changed is not relivant.
28562  *
28563  * Fork - LGPL
28564  * <script type="text/javascript">
28565  */
28566  
28567 /**
28568  * @class Roo.BasicDialog
28569  * @extends Roo.util.Observable
28570  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28571  * <pre><code>
28572 var dlg = new Roo.BasicDialog("my-dlg", {
28573     height: 200,
28574     width: 300,
28575     minHeight: 100,
28576     minWidth: 150,
28577     modal: true,
28578     proxyDrag: true,
28579     shadow: true
28580 });
28581 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28582 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28583 dlg.addButton('Cancel', dlg.hide, dlg);
28584 dlg.show();
28585 </code></pre>
28586   <b>A Dialog should always be a direct child of the body element.</b>
28587  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28588  * @cfg {String} title Default text to display in the title bar (defaults to null)
28589  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28590  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28591  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28592  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28593  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28594  * (defaults to null with no animation)
28595  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28596  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28597  * property for valid values (defaults to 'all')
28598  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28599  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28600  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28601  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28602  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28603  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28604  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28605  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28606  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28607  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28608  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28609  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28610  * draggable = true (defaults to false)
28611  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28612  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28613  * shadow (defaults to false)
28614  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28615  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28616  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28617  * @cfg {Array} buttons Array of buttons
28618  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28619  * @constructor
28620  * Create a new BasicDialog.
28621  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28622  * @param {Object} config Configuration options
28623  */
28624 Roo.BasicDialog = function(el, config){
28625     this.el = Roo.get(el);
28626     var dh = Roo.DomHelper;
28627     if(!this.el && config && config.autoCreate){
28628         if(typeof config.autoCreate == "object"){
28629             if(!config.autoCreate.id){
28630                 config.autoCreate.id = el;
28631             }
28632             this.el = dh.append(document.body,
28633                         config.autoCreate, true);
28634         }else{
28635             this.el = dh.append(document.body,
28636                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28637         }
28638     }
28639     el = this.el;
28640     el.setDisplayed(true);
28641     el.hide = this.hideAction;
28642     this.id = el.id;
28643     el.addClass("x-dlg");
28644
28645     Roo.apply(this, config);
28646
28647     this.proxy = el.createProxy("x-dlg-proxy");
28648     this.proxy.hide = this.hideAction;
28649     this.proxy.setOpacity(.5);
28650     this.proxy.hide();
28651
28652     if(config.width){
28653         el.setWidth(config.width);
28654     }
28655     if(config.height){
28656         el.setHeight(config.height);
28657     }
28658     this.size = el.getSize();
28659     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28660         this.xy = [config.x,config.y];
28661     }else{
28662         this.xy = el.getCenterXY(true);
28663     }
28664     /** The header element @type Roo.Element */
28665     this.header = el.child("> .x-dlg-hd");
28666     /** The body element @type Roo.Element */
28667     this.body = el.child("> .x-dlg-bd");
28668     /** The footer element @type Roo.Element */
28669     this.footer = el.child("> .x-dlg-ft");
28670
28671     if(!this.header){
28672         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28673     }
28674     if(!this.body){
28675         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28676     }
28677
28678     this.header.unselectable();
28679     if(this.title){
28680         this.header.update(this.title);
28681     }
28682     // this element allows the dialog to be focused for keyboard event
28683     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28684     this.focusEl.swallowEvent("click", true);
28685
28686     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28687
28688     // wrap the body and footer for special rendering
28689     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28690     if(this.footer){
28691         this.bwrap.dom.appendChild(this.footer.dom);
28692     }
28693
28694     this.bg = this.el.createChild({
28695         tag: "div", cls:"x-dlg-bg",
28696         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28697     });
28698     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28699
28700
28701     if(this.autoScroll !== false && !this.autoTabs){
28702         this.body.setStyle("overflow", "auto");
28703     }
28704
28705     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28706
28707     if(this.closable !== false){
28708         this.el.addClass("x-dlg-closable");
28709         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28710         this.close.on("click", this.closeClick, this);
28711         this.close.addClassOnOver("x-dlg-close-over");
28712     }
28713     if(this.collapsible !== false){
28714         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28715         this.collapseBtn.on("click", this.collapseClick, this);
28716         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28717         this.header.on("dblclick", this.collapseClick, this);
28718     }
28719     if(this.resizable !== false){
28720         this.el.addClass("x-dlg-resizable");
28721         this.resizer = new Roo.Resizable(el, {
28722             minWidth: this.minWidth || 80,
28723             minHeight:this.minHeight || 80,
28724             handles: this.resizeHandles || "all",
28725             pinned: true
28726         });
28727         this.resizer.on("beforeresize", this.beforeResize, this);
28728         this.resizer.on("resize", this.onResize, this);
28729     }
28730     if(this.draggable !== false){
28731         el.addClass("x-dlg-draggable");
28732         if (!this.proxyDrag) {
28733             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28734         }
28735         else {
28736             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28737         }
28738         dd.setHandleElId(this.header.id);
28739         dd.endDrag = this.endMove.createDelegate(this);
28740         dd.startDrag = this.startMove.createDelegate(this);
28741         dd.onDrag = this.onDrag.createDelegate(this);
28742         dd.scroll = false;
28743         this.dd = dd;
28744     }
28745     if(this.modal){
28746         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28747         this.mask.enableDisplayMode("block");
28748         this.mask.hide();
28749         this.el.addClass("x-dlg-modal");
28750     }
28751     if(this.shadow){
28752         this.shadow = new Roo.Shadow({
28753             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28754             offset : this.shadowOffset
28755         });
28756     }else{
28757         this.shadowOffset = 0;
28758     }
28759     if(Roo.useShims && this.shim !== false){
28760         this.shim = this.el.createShim();
28761         this.shim.hide = this.hideAction;
28762         this.shim.hide();
28763     }else{
28764         this.shim = false;
28765     }
28766     if(this.autoTabs){
28767         this.initTabs();
28768     }
28769     if (this.buttons) { 
28770         var bts= this.buttons;
28771         this.buttons = [];
28772         Roo.each(bts, function(b) {
28773             this.addButton(b);
28774         }, this);
28775     }
28776     
28777     
28778     this.addEvents({
28779         /**
28780          * @event keydown
28781          * Fires when a key is pressed
28782          * @param {Roo.BasicDialog} this
28783          * @param {Roo.EventObject} e
28784          */
28785         "keydown" : true,
28786         /**
28787          * @event move
28788          * Fires when this dialog is moved by the user.
28789          * @param {Roo.BasicDialog} this
28790          * @param {Number} x The new page X
28791          * @param {Number} y The new page Y
28792          */
28793         "move" : true,
28794         /**
28795          * @event resize
28796          * Fires when this dialog is resized by the user.
28797          * @param {Roo.BasicDialog} this
28798          * @param {Number} width The new width
28799          * @param {Number} height The new height
28800          */
28801         "resize" : true,
28802         /**
28803          * @event beforehide
28804          * Fires before this dialog is hidden.
28805          * @param {Roo.BasicDialog} this
28806          */
28807         "beforehide" : true,
28808         /**
28809          * @event hide
28810          * Fires when this dialog is hidden.
28811          * @param {Roo.BasicDialog} this
28812          */
28813         "hide" : true,
28814         /**
28815          * @event beforeshow
28816          * Fires before this dialog is shown.
28817          * @param {Roo.BasicDialog} this
28818          */
28819         "beforeshow" : true,
28820         /**
28821          * @event show
28822          * Fires when this dialog is shown.
28823          * @param {Roo.BasicDialog} this
28824          */
28825         "show" : true
28826     });
28827     el.on("keydown", this.onKeyDown, this);
28828     el.on("mousedown", this.toFront, this);
28829     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28830     this.el.hide();
28831     Roo.DialogManager.register(this);
28832     Roo.BasicDialog.superclass.constructor.call(this);
28833 };
28834
28835 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28836     shadowOffset: Roo.isIE ? 6 : 5,
28837     minHeight: 80,
28838     minWidth: 200,
28839     minButtonWidth: 75,
28840     defaultButton: null,
28841     buttonAlign: "right",
28842     tabTag: 'div',
28843     firstShow: true,
28844
28845     /**
28846      * Sets the dialog title text
28847      * @param {String} text The title text to display
28848      * @return {Roo.BasicDialog} this
28849      */
28850     setTitle : function(text){
28851         this.header.update(text);
28852         return this;
28853     },
28854
28855     // private
28856     closeClick : function(){
28857         this.hide();
28858     },
28859
28860     // private
28861     collapseClick : function(){
28862         this[this.collapsed ? "expand" : "collapse"]();
28863     },
28864
28865     /**
28866      * Collapses the dialog to its minimized state (only the title bar is visible).
28867      * Equivalent to the user clicking the collapse dialog button.
28868      */
28869     collapse : function(){
28870         if(!this.collapsed){
28871             this.collapsed = true;
28872             this.el.addClass("x-dlg-collapsed");
28873             this.restoreHeight = this.el.getHeight();
28874             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28875         }
28876     },
28877
28878     /**
28879      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28880      * clicking the expand dialog button.
28881      */
28882     expand : function(){
28883         if(this.collapsed){
28884             this.collapsed = false;
28885             this.el.removeClass("x-dlg-collapsed");
28886             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28887         }
28888     },
28889
28890     /**
28891      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28892      * @return {Roo.TabPanel} The tabs component
28893      */
28894     initTabs : function(){
28895         var tabs = this.getTabs();
28896         while(tabs.getTab(0)){
28897             tabs.removeTab(0);
28898         }
28899         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28900             var dom = el.dom;
28901             tabs.addTab(Roo.id(dom), dom.title);
28902             dom.title = "";
28903         });
28904         tabs.activate(0);
28905         return tabs;
28906     },
28907
28908     // private
28909     beforeResize : function(){
28910         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28911     },
28912
28913     // private
28914     onResize : function(){
28915         this.refreshSize();
28916         this.syncBodyHeight();
28917         this.adjustAssets();
28918         this.focus();
28919         this.fireEvent("resize", this, this.size.width, this.size.height);
28920     },
28921
28922     // private
28923     onKeyDown : function(e){
28924         if(this.isVisible()){
28925             this.fireEvent("keydown", this, e);
28926         }
28927     },
28928
28929     /**
28930      * Resizes the dialog.
28931      * @param {Number} width
28932      * @param {Number} height
28933      * @return {Roo.BasicDialog} this
28934      */
28935     resizeTo : function(width, height){
28936         this.el.setSize(width, height);
28937         this.size = {width: width, height: height};
28938         this.syncBodyHeight();
28939         if(this.fixedcenter){
28940             this.center();
28941         }
28942         if(this.isVisible()){
28943             this.constrainXY();
28944             this.adjustAssets();
28945         }
28946         this.fireEvent("resize", this, width, height);
28947         return this;
28948     },
28949
28950
28951     /**
28952      * Resizes the dialog to fit the specified content size.
28953      * @param {Number} width
28954      * @param {Number} height
28955      * @return {Roo.BasicDialog} this
28956      */
28957     setContentSize : function(w, h){
28958         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28959         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28960         //if(!this.el.isBorderBox()){
28961             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28962             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28963         //}
28964         if(this.tabs){
28965             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28966             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28967         }
28968         this.resizeTo(w, h);
28969         return this;
28970     },
28971
28972     /**
28973      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28974      * executed in response to a particular key being pressed while the dialog is active.
28975      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28976      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28977      * @param {Function} fn The function to call
28978      * @param {Object} scope (optional) The scope of the function
28979      * @return {Roo.BasicDialog} this
28980      */
28981     addKeyListener : function(key, fn, scope){
28982         var keyCode, shift, ctrl, alt;
28983         if(typeof key == "object" && !(key instanceof Array)){
28984             keyCode = key["key"];
28985             shift = key["shift"];
28986             ctrl = key["ctrl"];
28987             alt = key["alt"];
28988         }else{
28989             keyCode = key;
28990         }
28991         var handler = function(dlg, e){
28992             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28993                 var k = e.getKey();
28994                 if(keyCode instanceof Array){
28995                     for(var i = 0, len = keyCode.length; i < len; i++){
28996                         if(keyCode[i] == k){
28997                           fn.call(scope || window, dlg, k, e);
28998                           return;
28999                         }
29000                     }
29001                 }else{
29002                     if(k == keyCode){
29003                         fn.call(scope || window, dlg, k, e);
29004                     }
29005                 }
29006             }
29007         };
29008         this.on("keydown", handler);
29009         return this;
29010     },
29011
29012     /**
29013      * Returns the TabPanel component (creates it if it doesn't exist).
29014      * Note: If you wish to simply check for the existence of tabs without creating them,
29015      * check for a null 'tabs' property.
29016      * @return {Roo.TabPanel} The tabs component
29017      */
29018     getTabs : function(){
29019         if(!this.tabs){
29020             this.el.addClass("x-dlg-auto-tabs");
29021             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29022             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29023         }
29024         return this.tabs;
29025     },
29026
29027     /**
29028      * Adds a button to the footer section of the dialog.
29029      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29030      * object or a valid Roo.DomHelper element config
29031      * @param {Function} handler The function called when the button is clicked
29032      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29033      * @return {Roo.Button} The new button
29034      */
29035     addButton : function(config, handler, scope){
29036         var dh = Roo.DomHelper;
29037         if(!this.footer){
29038             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29039         }
29040         if(!this.btnContainer){
29041             var tb = this.footer.createChild({
29042
29043                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29044                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29045             }, null, true);
29046             this.btnContainer = tb.firstChild.firstChild.firstChild;
29047         }
29048         var bconfig = {
29049             handler: handler,
29050             scope: scope,
29051             minWidth: this.minButtonWidth,
29052             hideParent:true
29053         };
29054         if(typeof config == "string"){
29055             bconfig.text = config;
29056         }else{
29057             if(config.tag){
29058                 bconfig.dhconfig = config;
29059             }else{
29060                 Roo.apply(bconfig, config);
29061             }
29062         }
29063         var fc = false;
29064         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29065             bconfig.position = Math.max(0, bconfig.position);
29066             fc = this.btnContainer.childNodes[bconfig.position];
29067         }
29068          
29069         var btn = new Roo.Button(
29070             fc ? 
29071                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29072                 : this.btnContainer.appendChild(document.createElement("td")),
29073             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29074             bconfig
29075         );
29076         this.syncBodyHeight();
29077         if(!this.buttons){
29078             /**
29079              * Array of all the buttons that have been added to this dialog via addButton
29080              * @type Array
29081              */
29082             this.buttons = [];
29083         }
29084         this.buttons.push(btn);
29085         return btn;
29086     },
29087
29088     /**
29089      * Sets the default button to be focused when the dialog is displayed.
29090      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29091      * @return {Roo.BasicDialog} this
29092      */
29093     setDefaultButton : function(btn){
29094         this.defaultButton = btn;
29095         return this;
29096     },
29097
29098     // private
29099     getHeaderFooterHeight : function(safe){
29100         var height = 0;
29101         if(this.header){
29102            height += this.header.getHeight();
29103         }
29104         if(this.footer){
29105            var fm = this.footer.getMargins();
29106             height += (this.footer.getHeight()+fm.top+fm.bottom);
29107         }
29108         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29109         height += this.centerBg.getPadding("tb");
29110         return height;
29111     },
29112
29113     // private
29114     syncBodyHeight : function(){
29115         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29116         var height = this.size.height - this.getHeaderFooterHeight(false);
29117         bd.setHeight(height-bd.getMargins("tb"));
29118         var hh = this.header.getHeight();
29119         var h = this.size.height-hh;
29120         cb.setHeight(h);
29121         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29122         bw.setHeight(h-cb.getPadding("tb"));
29123         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29124         bd.setWidth(bw.getWidth(true));
29125         if(this.tabs){
29126             this.tabs.syncHeight();
29127             if(Roo.isIE){
29128                 this.tabs.el.repaint();
29129             }
29130         }
29131     },
29132
29133     /**
29134      * Restores the previous state of the dialog if Roo.state is configured.
29135      * @return {Roo.BasicDialog} this
29136      */
29137     restoreState : function(){
29138         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29139         if(box && box.width){
29140             this.xy = [box.x, box.y];
29141             this.resizeTo(box.width, box.height);
29142         }
29143         return this;
29144     },
29145
29146     // private
29147     beforeShow : function(){
29148         this.expand();
29149         if(this.fixedcenter){
29150             this.xy = this.el.getCenterXY(true);
29151         }
29152         if(this.modal){
29153             Roo.get(document.body).addClass("x-body-masked");
29154             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29155             this.mask.show();
29156         }
29157         this.constrainXY();
29158     },
29159
29160     // private
29161     animShow : function(){
29162         var b = Roo.get(this.animateTarget).getBox();
29163         this.proxy.setSize(b.width, b.height);
29164         this.proxy.setLocation(b.x, b.y);
29165         this.proxy.show();
29166         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29167                     true, .35, this.showEl.createDelegate(this));
29168     },
29169
29170     /**
29171      * Shows the dialog.
29172      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29173      * @return {Roo.BasicDialog} this
29174      */
29175     show : function(animateTarget){
29176         if (this.fireEvent("beforeshow", this) === false){
29177             return;
29178         }
29179         if(this.syncHeightBeforeShow){
29180             this.syncBodyHeight();
29181         }else if(this.firstShow){
29182             this.firstShow = false;
29183             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29184         }
29185         this.animateTarget = animateTarget || this.animateTarget;
29186         if(!this.el.isVisible()){
29187             this.beforeShow();
29188             if(this.animateTarget && Roo.get(this.animateTarget)){
29189                 this.animShow();
29190             }else{
29191                 this.showEl();
29192             }
29193         }
29194         return this;
29195     },
29196
29197     // private
29198     showEl : function(){
29199         this.proxy.hide();
29200         this.el.setXY(this.xy);
29201         this.el.show();
29202         this.adjustAssets(true);
29203         this.toFront();
29204         this.focus();
29205         // IE peekaboo bug - fix found by Dave Fenwick
29206         if(Roo.isIE){
29207             this.el.repaint();
29208         }
29209         this.fireEvent("show", this);
29210     },
29211
29212     /**
29213      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29214      * dialog itself will receive focus.
29215      */
29216     focus : function(){
29217         if(this.defaultButton){
29218             this.defaultButton.focus();
29219         }else{
29220             this.focusEl.focus();
29221         }
29222     },
29223
29224     // private
29225     constrainXY : function(){
29226         if(this.constraintoviewport !== false){
29227             if(!this.viewSize){
29228                 if(this.container){
29229                     var s = this.container.getSize();
29230                     this.viewSize = [s.width, s.height];
29231                 }else{
29232                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29233                 }
29234             }
29235             var s = Roo.get(this.container||document).getScroll();
29236
29237             var x = this.xy[0], y = this.xy[1];
29238             var w = this.size.width, h = this.size.height;
29239             var vw = this.viewSize[0], vh = this.viewSize[1];
29240             // only move it if it needs it
29241             var moved = false;
29242             // first validate right/bottom
29243             if(x + w > vw+s.left){
29244                 x = vw - w;
29245                 moved = true;
29246             }
29247             if(y + h > vh+s.top){
29248                 y = vh - h;
29249                 moved = true;
29250             }
29251             // then make sure top/left isn't negative
29252             if(x < s.left){
29253                 x = s.left;
29254                 moved = true;
29255             }
29256             if(y < s.top){
29257                 y = s.top;
29258                 moved = true;
29259             }
29260             if(moved){
29261                 // cache xy
29262                 this.xy = [x, y];
29263                 if(this.isVisible()){
29264                     this.el.setLocation(x, y);
29265                     this.adjustAssets();
29266                 }
29267             }
29268         }
29269     },
29270
29271     // private
29272     onDrag : function(){
29273         if(!this.proxyDrag){
29274             this.xy = this.el.getXY();
29275             this.adjustAssets();
29276         }
29277     },
29278
29279     // private
29280     adjustAssets : function(doShow){
29281         var x = this.xy[0], y = this.xy[1];
29282         var w = this.size.width, h = this.size.height;
29283         if(doShow === true){
29284             if(this.shadow){
29285                 this.shadow.show(this.el);
29286             }
29287             if(this.shim){
29288                 this.shim.show();
29289             }
29290         }
29291         if(this.shadow && this.shadow.isVisible()){
29292             this.shadow.show(this.el);
29293         }
29294         if(this.shim && this.shim.isVisible()){
29295             this.shim.setBounds(x, y, w, h);
29296         }
29297     },
29298
29299     // private
29300     adjustViewport : function(w, h){
29301         if(!w || !h){
29302             w = Roo.lib.Dom.getViewWidth();
29303             h = Roo.lib.Dom.getViewHeight();
29304         }
29305         // cache the size
29306         this.viewSize = [w, h];
29307         if(this.modal && this.mask.isVisible()){
29308             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29309             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29310         }
29311         if(this.isVisible()){
29312             this.constrainXY();
29313         }
29314     },
29315
29316     /**
29317      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29318      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29319      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29320      */
29321     destroy : function(removeEl){
29322         if(this.isVisible()){
29323             this.animateTarget = null;
29324             this.hide();
29325         }
29326         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29327         if(this.tabs){
29328             this.tabs.destroy(removeEl);
29329         }
29330         Roo.destroy(
29331              this.shim,
29332              this.proxy,
29333              this.resizer,
29334              this.close,
29335              this.mask
29336         );
29337         if(this.dd){
29338             this.dd.unreg();
29339         }
29340         if(this.buttons){
29341            for(var i = 0, len = this.buttons.length; i < len; i++){
29342                this.buttons[i].destroy();
29343            }
29344         }
29345         this.el.removeAllListeners();
29346         if(removeEl === true){
29347             this.el.update("");
29348             this.el.remove();
29349         }
29350         Roo.DialogManager.unregister(this);
29351     },
29352
29353     // private
29354     startMove : function(){
29355         if(this.proxyDrag){
29356             this.proxy.show();
29357         }
29358         if(this.constraintoviewport !== false){
29359             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29360         }
29361     },
29362
29363     // private
29364     endMove : function(){
29365         if(!this.proxyDrag){
29366             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29367         }else{
29368             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29369             this.proxy.hide();
29370         }
29371         this.refreshSize();
29372         this.adjustAssets();
29373         this.focus();
29374         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29375     },
29376
29377     /**
29378      * Brings this dialog to the front of any other visible dialogs
29379      * @return {Roo.BasicDialog} this
29380      */
29381     toFront : function(){
29382         Roo.DialogManager.bringToFront(this);
29383         return this;
29384     },
29385
29386     /**
29387      * Sends this dialog to the back (under) of any other visible dialogs
29388      * @return {Roo.BasicDialog} this
29389      */
29390     toBack : function(){
29391         Roo.DialogManager.sendToBack(this);
29392         return this;
29393     },
29394
29395     /**
29396      * Centers this dialog in the viewport
29397      * @return {Roo.BasicDialog} this
29398      */
29399     center : function(){
29400         var xy = this.el.getCenterXY(true);
29401         this.moveTo(xy[0], xy[1]);
29402         return this;
29403     },
29404
29405     /**
29406      * Moves the dialog's top-left corner to the specified point
29407      * @param {Number} x
29408      * @param {Number} y
29409      * @return {Roo.BasicDialog} this
29410      */
29411     moveTo : function(x, y){
29412         this.xy = [x,y];
29413         if(this.isVisible()){
29414             this.el.setXY(this.xy);
29415             this.adjustAssets();
29416         }
29417         return this;
29418     },
29419
29420     /**
29421      * Aligns the dialog to the specified element
29422      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29423      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29424      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29425      * @return {Roo.BasicDialog} this
29426      */
29427     alignTo : function(element, position, offsets){
29428         this.xy = this.el.getAlignToXY(element, position, offsets);
29429         if(this.isVisible()){
29430             this.el.setXY(this.xy);
29431             this.adjustAssets();
29432         }
29433         return this;
29434     },
29435
29436     /**
29437      * Anchors an element to another element and realigns it when the window is resized.
29438      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29439      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29440      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29441      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29442      * is a number, it is used as the buffer delay (defaults to 50ms).
29443      * @return {Roo.BasicDialog} this
29444      */
29445     anchorTo : function(el, alignment, offsets, monitorScroll){
29446         var action = function(){
29447             this.alignTo(el, alignment, offsets);
29448         };
29449         Roo.EventManager.onWindowResize(action, this);
29450         var tm = typeof monitorScroll;
29451         if(tm != 'undefined'){
29452             Roo.EventManager.on(window, 'scroll', action, this,
29453                 {buffer: tm == 'number' ? monitorScroll : 50});
29454         }
29455         action.call(this);
29456         return this;
29457     },
29458
29459     /**
29460      * Returns true if the dialog is visible
29461      * @return {Boolean}
29462      */
29463     isVisible : function(){
29464         return this.el.isVisible();
29465     },
29466
29467     // private
29468     animHide : function(callback){
29469         var b = Roo.get(this.animateTarget).getBox();
29470         this.proxy.show();
29471         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29472         this.el.hide();
29473         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29474                     this.hideEl.createDelegate(this, [callback]));
29475     },
29476
29477     /**
29478      * Hides the dialog.
29479      * @param {Function} callback (optional) Function to call when the dialog is hidden
29480      * @return {Roo.BasicDialog} this
29481      */
29482     hide : function(callback){
29483         if (this.fireEvent("beforehide", this) === false){
29484             return;
29485         }
29486         if(this.shadow){
29487             this.shadow.hide();
29488         }
29489         if(this.shim) {
29490           this.shim.hide();
29491         }
29492         // sometimes animateTarget seems to get set.. causing problems...
29493         // this just double checks..
29494         if(this.animateTarget && Roo.get(this.animateTarget)) {
29495            this.animHide(callback);
29496         }else{
29497             this.el.hide();
29498             this.hideEl(callback);
29499         }
29500         return this;
29501     },
29502
29503     // private
29504     hideEl : function(callback){
29505         this.proxy.hide();
29506         if(this.modal){
29507             this.mask.hide();
29508             Roo.get(document.body).removeClass("x-body-masked");
29509         }
29510         this.fireEvent("hide", this);
29511         if(typeof callback == "function"){
29512             callback();
29513         }
29514     },
29515
29516     // private
29517     hideAction : function(){
29518         this.setLeft("-10000px");
29519         this.setTop("-10000px");
29520         this.setStyle("visibility", "hidden");
29521     },
29522
29523     // private
29524     refreshSize : function(){
29525         this.size = this.el.getSize();
29526         this.xy = this.el.getXY();
29527         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29528     },
29529
29530     // private
29531     // z-index is managed by the DialogManager and may be overwritten at any time
29532     setZIndex : function(index){
29533         if(this.modal){
29534             this.mask.setStyle("z-index", index);
29535         }
29536         if(this.shim){
29537             this.shim.setStyle("z-index", ++index);
29538         }
29539         if(this.shadow){
29540             this.shadow.setZIndex(++index);
29541         }
29542         this.el.setStyle("z-index", ++index);
29543         if(this.proxy){
29544             this.proxy.setStyle("z-index", ++index);
29545         }
29546         if(this.resizer){
29547             this.resizer.proxy.setStyle("z-index", ++index);
29548         }
29549
29550         this.lastZIndex = index;
29551     },
29552
29553     /**
29554      * Returns the element for this dialog
29555      * @return {Roo.Element} The underlying dialog Element
29556      */
29557     getEl : function(){
29558         return this.el;
29559     }
29560 });
29561
29562 /**
29563  * @class Roo.DialogManager
29564  * Provides global access to BasicDialogs that have been created and
29565  * support for z-indexing (layering) multiple open dialogs.
29566  */
29567 Roo.DialogManager = function(){
29568     var list = {};
29569     var accessList = [];
29570     var front = null;
29571
29572     // private
29573     var sortDialogs = function(d1, d2){
29574         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29575     };
29576
29577     // private
29578     var orderDialogs = function(){
29579         accessList.sort(sortDialogs);
29580         var seed = Roo.DialogManager.zseed;
29581         for(var i = 0, len = accessList.length; i < len; i++){
29582             var dlg = accessList[i];
29583             if(dlg){
29584                 dlg.setZIndex(seed + (i*10));
29585             }
29586         }
29587     };
29588
29589     return {
29590         /**
29591          * The starting z-index for BasicDialogs (defaults to 9000)
29592          * @type Number The z-index value
29593          */
29594         zseed : 9000,
29595
29596         // private
29597         register : function(dlg){
29598             list[dlg.id] = dlg;
29599             accessList.push(dlg);
29600         },
29601
29602         // private
29603         unregister : function(dlg){
29604             delete list[dlg.id];
29605             var i=0;
29606             var len=0;
29607             if(!accessList.indexOf){
29608                 for(  i = 0, len = accessList.length; i < len; i++){
29609                     if(accessList[i] == dlg){
29610                         accessList.splice(i, 1);
29611                         return;
29612                     }
29613                 }
29614             }else{
29615                  i = accessList.indexOf(dlg);
29616                 if(i != -1){
29617                     accessList.splice(i, 1);
29618                 }
29619             }
29620         },
29621
29622         /**
29623          * Gets a registered dialog by id
29624          * @param {String/Object} id The id of the dialog or a dialog
29625          * @return {Roo.BasicDialog} this
29626          */
29627         get : function(id){
29628             return typeof id == "object" ? id : list[id];
29629         },
29630
29631         /**
29632          * Brings the specified dialog to the front
29633          * @param {String/Object} dlg The id of the dialog or a dialog
29634          * @return {Roo.BasicDialog} this
29635          */
29636         bringToFront : function(dlg){
29637             dlg = this.get(dlg);
29638             if(dlg != front){
29639                 front = dlg;
29640                 dlg._lastAccess = new Date().getTime();
29641                 orderDialogs();
29642             }
29643             return dlg;
29644         },
29645
29646         /**
29647          * Sends the specified dialog to the back
29648          * @param {String/Object} dlg The id of the dialog or a dialog
29649          * @return {Roo.BasicDialog} this
29650          */
29651         sendToBack : function(dlg){
29652             dlg = this.get(dlg);
29653             dlg._lastAccess = -(new Date().getTime());
29654             orderDialogs();
29655             return dlg;
29656         },
29657
29658         /**
29659          * Hides all dialogs
29660          */
29661         hideAll : function(){
29662             for(var id in list){
29663                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29664                     list[id].hide();
29665                 }
29666             }
29667         }
29668     };
29669 }();
29670
29671 /**
29672  * @class Roo.LayoutDialog
29673  * @extends Roo.BasicDialog
29674  * Dialog which provides adjustments for working with a layout in a Dialog.
29675  * Add your necessary layout config options to the dialog's config.<br>
29676  * Example usage (including a nested layout):
29677  * <pre><code>
29678 if(!dialog){
29679     dialog = new Roo.LayoutDialog("download-dlg", {
29680         modal: true,
29681         width:600,
29682         height:450,
29683         shadow:true,
29684         minWidth:500,
29685         minHeight:350,
29686         autoTabs:true,
29687         proxyDrag:true,
29688         // layout config merges with the dialog config
29689         center:{
29690             tabPosition: "top",
29691             alwaysShowTabs: true
29692         }
29693     });
29694     dialog.addKeyListener(27, dialog.hide, dialog);
29695     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29696     dialog.addButton("Build It!", this.getDownload, this);
29697
29698     // we can even add nested layouts
29699     var innerLayout = new Roo.BorderLayout("dl-inner", {
29700         east: {
29701             initialSize: 200,
29702             autoScroll:true,
29703             split:true
29704         },
29705         center: {
29706             autoScroll:true
29707         }
29708     });
29709     innerLayout.beginUpdate();
29710     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29711     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29712     innerLayout.endUpdate(true);
29713
29714     var layout = dialog.getLayout();
29715     layout.beginUpdate();
29716     layout.add("center", new Roo.ContentPanel("standard-panel",
29717                         {title: "Download the Source", fitToFrame:true}));
29718     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29719                {title: "Build your own roo.js"}));
29720     layout.getRegion("center").showPanel(sp);
29721     layout.endUpdate();
29722 }
29723 </code></pre>
29724     * @constructor
29725     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29726     * @param {Object} config configuration options
29727   */
29728 Roo.LayoutDialog = function(el, cfg){
29729     
29730     var config=  cfg;
29731     if (typeof(cfg) == 'undefined') {
29732         config = Roo.apply({}, el);
29733         // not sure why we use documentElement here.. - it should always be body.
29734         // IE7 borks horribly if we use documentElement.
29735         // webkit also does not like documentElement - it creates a body element...
29736         el = Roo.get( document.body || document.documentElement ).createChild();
29737         //config.autoCreate = true;
29738     }
29739     
29740     
29741     config.autoTabs = false;
29742     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29743     this.body.setStyle({overflow:"hidden", position:"relative"});
29744     this.layout = new Roo.BorderLayout(this.body.dom, config);
29745     this.layout.monitorWindowResize = false;
29746     this.el.addClass("x-dlg-auto-layout");
29747     // fix case when center region overwrites center function
29748     this.center = Roo.BasicDialog.prototype.center;
29749     this.on("show", this.layout.layout, this.layout, true);
29750     if (config.items) {
29751         var xitems = config.items;
29752         delete config.items;
29753         Roo.each(xitems, this.addxtype, this);
29754     }
29755     
29756     
29757 };
29758 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29759     /**
29760      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29761      * @deprecated
29762      */
29763     endUpdate : function(){
29764         this.layout.endUpdate();
29765     },
29766
29767     /**
29768      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29769      *  @deprecated
29770      */
29771     beginUpdate : function(){
29772         this.layout.beginUpdate();
29773     },
29774
29775     /**
29776      * Get the BorderLayout for this dialog
29777      * @return {Roo.BorderLayout}
29778      */
29779     getLayout : function(){
29780         return this.layout;
29781     },
29782
29783     showEl : function(){
29784         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29785         if(Roo.isIE7){
29786             this.layout.layout();
29787         }
29788     },
29789
29790     // private
29791     // Use the syncHeightBeforeShow config option to control this automatically
29792     syncBodyHeight : function(){
29793         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29794         if(this.layout){this.layout.layout();}
29795     },
29796     
29797       /**
29798      * Add an xtype element (actually adds to the layout.)
29799      * @return {Object} xdata xtype object data.
29800      */
29801     
29802     addxtype : function(c) {
29803         return this.layout.addxtype(c);
29804     }
29805 });/*
29806  * Based on:
29807  * Ext JS Library 1.1.1
29808  * Copyright(c) 2006-2007, Ext JS, LLC.
29809  *
29810  * Originally Released Under LGPL - original licence link has changed is not relivant.
29811  *
29812  * Fork - LGPL
29813  * <script type="text/javascript">
29814  */
29815  
29816 /**
29817  * @class Roo.MessageBox
29818  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29819  * Example usage:
29820  *<pre><code>
29821 // Basic alert:
29822 Roo.Msg.alert('Status', 'Changes saved successfully.');
29823
29824 // Prompt for user data:
29825 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29826     if (btn == 'ok'){
29827         // process text value...
29828     }
29829 });
29830
29831 // Show a dialog using config options:
29832 Roo.Msg.show({
29833    title:'Save Changes?',
29834    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29835    buttons: Roo.Msg.YESNOCANCEL,
29836    fn: processResult,
29837    animEl: 'elId'
29838 });
29839 </code></pre>
29840  * @singleton
29841  */
29842 Roo.MessageBox = function(){
29843     var dlg, opt, mask, waitTimer;
29844     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29845     var buttons, activeTextEl, bwidth;
29846
29847     // private
29848     var handleButton = function(button){
29849         dlg.hide();
29850         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29851     };
29852
29853     // private
29854     var handleHide = function(){
29855         if(opt && opt.cls){
29856             dlg.el.removeClass(opt.cls);
29857         }
29858         if(waitTimer){
29859             Roo.TaskMgr.stop(waitTimer);
29860             waitTimer = null;
29861         }
29862     };
29863
29864     // private
29865     var updateButtons = function(b){
29866         var width = 0;
29867         if(!b){
29868             buttons["ok"].hide();
29869             buttons["cancel"].hide();
29870             buttons["yes"].hide();
29871             buttons["no"].hide();
29872             dlg.footer.dom.style.display = 'none';
29873             return width;
29874         }
29875         dlg.footer.dom.style.display = '';
29876         for(var k in buttons){
29877             if(typeof buttons[k] != "function"){
29878                 if(b[k]){
29879                     buttons[k].show();
29880                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29881                     width += buttons[k].el.getWidth()+15;
29882                 }else{
29883                     buttons[k].hide();
29884                 }
29885             }
29886         }
29887         return width;
29888     };
29889
29890     // private
29891     var handleEsc = function(d, k, e){
29892         if(opt && opt.closable !== false){
29893             dlg.hide();
29894         }
29895         if(e){
29896             e.stopEvent();
29897         }
29898     };
29899
29900     return {
29901         /**
29902          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29903          * @return {Roo.BasicDialog} The BasicDialog element
29904          */
29905         getDialog : function(){
29906            if(!dlg){
29907                 dlg = new Roo.BasicDialog("x-msg-box", {
29908                     autoCreate : true,
29909                     shadow: true,
29910                     draggable: true,
29911                     resizable:false,
29912                     constraintoviewport:false,
29913                     fixedcenter:true,
29914                     collapsible : false,
29915                     shim:true,
29916                     modal: true,
29917                     width:400, height:100,
29918                     buttonAlign:"center",
29919                     closeClick : function(){
29920                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29921                             handleButton("no");
29922                         }else{
29923                             handleButton("cancel");
29924                         }
29925                     }
29926                 });
29927                 dlg.on("hide", handleHide);
29928                 mask = dlg.mask;
29929                 dlg.addKeyListener(27, handleEsc);
29930                 buttons = {};
29931                 var bt = this.buttonText;
29932                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29933                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29934                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29935                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29936                 bodyEl = dlg.body.createChild({
29937
29938                     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>'
29939                 });
29940                 msgEl = bodyEl.dom.firstChild;
29941                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29942                 textboxEl.enableDisplayMode();
29943                 textboxEl.addKeyListener([10,13], function(){
29944                     if(dlg.isVisible() && opt && opt.buttons){
29945                         if(opt.buttons.ok){
29946                             handleButton("ok");
29947                         }else if(opt.buttons.yes){
29948                             handleButton("yes");
29949                         }
29950                     }
29951                 });
29952                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29953                 textareaEl.enableDisplayMode();
29954                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29955                 progressEl.enableDisplayMode();
29956                 var pf = progressEl.dom.firstChild;
29957                 if (pf) {
29958                     pp = Roo.get(pf.firstChild);
29959                     pp.setHeight(pf.offsetHeight);
29960                 }
29961                 
29962             }
29963             return dlg;
29964         },
29965
29966         /**
29967          * Updates the message box body text
29968          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29969          * the XHTML-compliant non-breaking space character '&amp;#160;')
29970          * @return {Roo.MessageBox} This message box
29971          */
29972         updateText : function(text){
29973             if(!dlg.isVisible() && !opt.width){
29974                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29975             }
29976             msgEl.innerHTML = text || '&#160;';
29977             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29978                         Math.max(opt.minWidth || this.minWidth, bwidth));
29979             if(opt.prompt){
29980                 activeTextEl.setWidth(w);
29981             }
29982             if(dlg.isVisible()){
29983                 dlg.fixedcenter = false;
29984             }
29985             dlg.setContentSize(w, bodyEl.getHeight());
29986             if(dlg.isVisible()){
29987                 dlg.fixedcenter = true;
29988             }
29989             return this;
29990         },
29991
29992         /**
29993          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29994          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29995          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29996          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29997          * @return {Roo.MessageBox} This message box
29998          */
29999         updateProgress : function(value, text){
30000             if(text){
30001                 this.updateText(text);
30002             }
30003             if (pp) { // weird bug on my firefox - for some reason this is not defined
30004                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30005             }
30006             return this;
30007         },        
30008
30009         /**
30010          * Returns true if the message box is currently displayed
30011          * @return {Boolean} True if the message box is visible, else false
30012          */
30013         isVisible : function(){
30014             return dlg && dlg.isVisible();  
30015         },
30016
30017         /**
30018          * Hides the message box if it is displayed
30019          */
30020         hide : function(){
30021             if(this.isVisible()){
30022                 dlg.hide();
30023             }  
30024         },
30025
30026         /**
30027          * Displays a new message box, or reinitializes an existing message box, based on the config options
30028          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30029          * The following config object properties are supported:
30030          * <pre>
30031 Property    Type             Description
30032 ----------  ---------------  ------------------------------------------------------------------------------------
30033 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30034                                    closes (defaults to undefined)
30035 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30036                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30037 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30038                                    progress and wait dialogs will ignore this property and always hide the
30039                                    close button as they can only be closed programmatically.
30040 cls               String           A custom CSS class to apply to the message box element
30041 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30042                                    displayed (defaults to 75)
30043 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30044                                    function will be btn (the name of the button that was clicked, if applicable,
30045                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30046                                    Progress and wait dialogs will ignore this option since they do not respond to
30047                                    user actions and can only be closed programmatically, so any required function
30048                                    should be called by the same code after it closes the dialog.
30049 icon              String           A CSS class that provides a background image to be used as an icon for
30050                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30051 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30052 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30053 modal             Boolean          False to allow user interaction with the page while the message box is
30054                                    displayed (defaults to true)
30055 msg               String           A string that will replace the existing message box body text (defaults
30056                                    to the XHTML-compliant non-breaking space character '&#160;')
30057 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30058 progress          Boolean          True to display a progress bar (defaults to false)
30059 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30060 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30061 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30062 title             String           The title text
30063 value             String           The string value to set into the active textbox element if displayed
30064 wait              Boolean          True to display a progress bar (defaults to false)
30065 width             Number           The width of the dialog in pixels
30066 </pre>
30067          *
30068          * Example usage:
30069          * <pre><code>
30070 Roo.Msg.show({
30071    title: 'Address',
30072    msg: 'Please enter your address:',
30073    width: 300,
30074    buttons: Roo.MessageBox.OKCANCEL,
30075    multiline: true,
30076    fn: saveAddress,
30077    animEl: 'addAddressBtn'
30078 });
30079 </code></pre>
30080          * @param {Object} config Configuration options
30081          * @return {Roo.MessageBox} This message box
30082          */
30083         show : function(options){
30084             if(this.isVisible()){
30085                 this.hide();
30086             }
30087             var d = this.getDialog();
30088             opt = options;
30089             d.setTitle(opt.title || "&#160;");
30090             d.close.setDisplayed(opt.closable !== false);
30091             activeTextEl = textboxEl;
30092             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30093             if(opt.prompt){
30094                 if(opt.multiline){
30095                     textboxEl.hide();
30096                     textareaEl.show();
30097                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30098                         opt.multiline : this.defaultTextHeight);
30099                     activeTextEl = textareaEl;
30100                 }else{
30101                     textboxEl.show();
30102                     textareaEl.hide();
30103                 }
30104             }else{
30105                 textboxEl.hide();
30106                 textareaEl.hide();
30107             }
30108             progressEl.setDisplayed(opt.progress === true);
30109             this.updateProgress(0);
30110             activeTextEl.dom.value = opt.value || "";
30111             if(opt.prompt){
30112                 dlg.setDefaultButton(activeTextEl);
30113             }else{
30114                 var bs = opt.buttons;
30115                 var db = null;
30116                 if(bs && bs.ok){
30117                     db = buttons["ok"];
30118                 }else if(bs && bs.yes){
30119                     db = buttons["yes"];
30120                 }
30121                 dlg.setDefaultButton(db);
30122             }
30123             bwidth = updateButtons(opt.buttons);
30124             this.updateText(opt.msg);
30125             if(opt.cls){
30126                 d.el.addClass(opt.cls);
30127             }
30128             d.proxyDrag = opt.proxyDrag === true;
30129             d.modal = opt.modal !== false;
30130             d.mask = opt.modal !== false ? mask : false;
30131             if(!d.isVisible()){
30132                 // force it to the end of the z-index stack so it gets a cursor in FF
30133                 document.body.appendChild(dlg.el.dom);
30134                 d.animateTarget = null;
30135                 d.show(options.animEl);
30136             }
30137             return this;
30138         },
30139
30140         /**
30141          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30142          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30143          * and closing the message box when the process is complete.
30144          * @param {String} title The title bar text
30145          * @param {String} msg The message box body text
30146          * @return {Roo.MessageBox} This message box
30147          */
30148         progress : function(title, msg){
30149             this.show({
30150                 title : title,
30151                 msg : msg,
30152                 buttons: false,
30153                 progress:true,
30154                 closable:false,
30155                 minWidth: this.minProgressWidth,
30156                 modal : true
30157             });
30158             return this;
30159         },
30160
30161         /**
30162          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30163          * If a callback function is passed it will be called after the user clicks the button, and the
30164          * id of the button that was clicked will be passed as the only parameter to the callback
30165          * (could also be the top-right close button).
30166          * @param {String} title The title bar text
30167          * @param {String} msg The message box body text
30168          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30169          * @param {Object} scope (optional) The scope of the callback function
30170          * @return {Roo.MessageBox} This message box
30171          */
30172         alert : function(title, msg, fn, scope){
30173             this.show({
30174                 title : title,
30175                 msg : msg,
30176                 buttons: this.OK,
30177                 fn: fn,
30178                 scope : scope,
30179                 modal : true
30180             });
30181             return this;
30182         },
30183
30184         /**
30185          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30186          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30187          * You are responsible for closing the message box when the process is complete.
30188          * @param {String} msg The message box body text
30189          * @param {String} title (optional) The title bar text
30190          * @return {Roo.MessageBox} This message box
30191          */
30192         wait : function(msg, title){
30193             this.show({
30194                 title : title,
30195                 msg : msg,
30196                 buttons: false,
30197                 closable:false,
30198                 progress:true,
30199                 modal:true,
30200                 width:300,
30201                 wait:true
30202             });
30203             waitTimer = Roo.TaskMgr.start({
30204                 run: function(i){
30205                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30206                 },
30207                 interval: 1000
30208             });
30209             return this;
30210         },
30211
30212         /**
30213          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30214          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30215          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30216          * @param {String} title The title bar text
30217          * @param {String} msg The message box body text
30218          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30219          * @param {Object} scope (optional) The scope of the callback function
30220          * @return {Roo.MessageBox} This message box
30221          */
30222         confirm : function(title, msg, fn, scope){
30223             this.show({
30224                 title : title,
30225                 msg : msg,
30226                 buttons: this.YESNO,
30227                 fn: fn,
30228                 scope : scope,
30229                 modal : true
30230             });
30231             return this;
30232         },
30233
30234         /**
30235          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30236          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30237          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30238          * (could also be the top-right close button) and the text that was entered will be passed as the two
30239          * parameters to the callback.
30240          * @param {String} title The title bar text
30241          * @param {String} msg The message box body text
30242          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30243          * @param {Object} scope (optional) The scope of the callback function
30244          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30245          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30246          * @return {Roo.MessageBox} This message box
30247          */
30248         prompt : function(title, msg, fn, scope, multiline){
30249             this.show({
30250                 title : title,
30251                 msg : msg,
30252                 buttons: this.OKCANCEL,
30253                 fn: fn,
30254                 minWidth:250,
30255                 scope : scope,
30256                 prompt:true,
30257                 multiline: multiline,
30258                 modal : true
30259             });
30260             return this;
30261         },
30262
30263         /**
30264          * Button config that displays a single OK button
30265          * @type Object
30266          */
30267         OK : {ok:true},
30268         /**
30269          * Button config that displays Yes and No buttons
30270          * @type Object
30271          */
30272         YESNO : {yes:true, no:true},
30273         /**
30274          * Button config that displays OK and Cancel buttons
30275          * @type Object
30276          */
30277         OKCANCEL : {ok:true, cancel:true},
30278         /**
30279          * Button config that displays Yes, No and Cancel buttons
30280          * @type Object
30281          */
30282         YESNOCANCEL : {yes:true, no:true, cancel:true},
30283
30284         /**
30285          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30286          * @type Number
30287          */
30288         defaultTextHeight : 75,
30289         /**
30290          * The maximum width in pixels of the message box (defaults to 600)
30291          * @type Number
30292          */
30293         maxWidth : 600,
30294         /**
30295          * The minimum width in pixels of the message box (defaults to 100)
30296          * @type Number
30297          */
30298         minWidth : 100,
30299         /**
30300          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30301          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30302          * @type Number
30303          */
30304         minProgressWidth : 250,
30305         /**
30306          * An object containing the default button text strings that can be overriden for localized language support.
30307          * Supported properties are: ok, cancel, yes and no.
30308          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30309          * @type Object
30310          */
30311         buttonText : {
30312             ok : "OK",
30313             cancel : "Cancel",
30314             yes : "Yes",
30315             no : "No"
30316         }
30317     };
30318 }();
30319
30320 /**
30321  * Shorthand for {@link Roo.MessageBox}
30322  */
30323 Roo.Msg = Roo.MessageBox;/*
30324  * Based on:
30325  * Ext JS Library 1.1.1
30326  * Copyright(c) 2006-2007, Ext JS, LLC.
30327  *
30328  * Originally Released Under LGPL - original licence link has changed is not relivant.
30329  *
30330  * Fork - LGPL
30331  * <script type="text/javascript">
30332  */
30333 /**
30334  * @class Roo.QuickTips
30335  * Provides attractive and customizable tooltips for any element.
30336  * @singleton
30337  */
30338 Roo.QuickTips = function(){
30339     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30340     var ce, bd, xy, dd;
30341     var visible = false, disabled = true, inited = false;
30342     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30343     
30344     var onOver = function(e){
30345         if(disabled){
30346             return;
30347         }
30348         var t = e.getTarget();
30349         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30350             return;
30351         }
30352         if(ce && t == ce.el){
30353             clearTimeout(hideProc);
30354             return;
30355         }
30356         if(t && tagEls[t.id]){
30357             tagEls[t.id].el = t;
30358             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30359             return;
30360         }
30361         var ttp, et = Roo.fly(t);
30362         var ns = cfg.namespace;
30363         if(tm.interceptTitles && t.title){
30364             ttp = t.title;
30365             t.qtip = ttp;
30366             t.removeAttribute("title");
30367             e.preventDefault();
30368         }else{
30369             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30370         }
30371         if(ttp){
30372             showProc = show.defer(tm.showDelay, tm, [{
30373                 el: t, 
30374                 text: ttp, 
30375                 width: et.getAttributeNS(ns, cfg.width),
30376                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30377                 title: et.getAttributeNS(ns, cfg.title),
30378                     cls: et.getAttributeNS(ns, cfg.cls)
30379             }]);
30380         }
30381     };
30382     
30383     var onOut = function(e){
30384         clearTimeout(showProc);
30385         var t = e.getTarget();
30386         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30387             hideProc = setTimeout(hide, tm.hideDelay);
30388         }
30389     };
30390     
30391     var onMove = function(e){
30392         if(disabled){
30393             return;
30394         }
30395         xy = e.getXY();
30396         xy[1] += 18;
30397         if(tm.trackMouse && ce){
30398             el.setXY(xy);
30399         }
30400     };
30401     
30402     var onDown = function(e){
30403         clearTimeout(showProc);
30404         clearTimeout(hideProc);
30405         if(!e.within(el)){
30406             if(tm.hideOnClick){
30407                 hide();
30408                 tm.disable();
30409                 tm.enable.defer(100, tm);
30410             }
30411         }
30412     };
30413     
30414     var getPad = function(){
30415         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30416     };
30417
30418     var show = function(o){
30419         if(disabled){
30420             return;
30421         }
30422         clearTimeout(dismissProc);
30423         ce = o;
30424         if(removeCls){ // in case manually hidden
30425             el.removeClass(removeCls);
30426             removeCls = null;
30427         }
30428         if(ce.cls){
30429             el.addClass(ce.cls);
30430             removeCls = ce.cls;
30431         }
30432         if(ce.title){
30433             tipTitle.update(ce.title);
30434             tipTitle.show();
30435         }else{
30436             tipTitle.update('');
30437             tipTitle.hide();
30438         }
30439         el.dom.style.width  = tm.maxWidth+'px';
30440         //tipBody.dom.style.width = '';
30441         tipBodyText.update(o.text);
30442         var p = getPad(), w = ce.width;
30443         if(!w){
30444             var td = tipBodyText.dom;
30445             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30446             if(aw > tm.maxWidth){
30447                 w = tm.maxWidth;
30448             }else if(aw < tm.minWidth){
30449                 w = tm.minWidth;
30450             }else{
30451                 w = aw;
30452             }
30453         }
30454         //tipBody.setWidth(w);
30455         el.setWidth(parseInt(w, 10) + p);
30456         if(ce.autoHide === false){
30457             close.setDisplayed(true);
30458             if(dd){
30459                 dd.unlock();
30460             }
30461         }else{
30462             close.setDisplayed(false);
30463             if(dd){
30464                 dd.lock();
30465             }
30466         }
30467         if(xy){
30468             el.avoidY = xy[1]-18;
30469             el.setXY(xy);
30470         }
30471         if(tm.animate){
30472             el.setOpacity(.1);
30473             el.setStyle("visibility", "visible");
30474             el.fadeIn({callback: afterShow});
30475         }else{
30476             afterShow();
30477         }
30478     };
30479     
30480     var afterShow = function(){
30481         if(ce){
30482             el.show();
30483             esc.enable();
30484             if(tm.autoDismiss && ce.autoHide !== false){
30485                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30486             }
30487         }
30488     };
30489     
30490     var hide = function(noanim){
30491         clearTimeout(dismissProc);
30492         clearTimeout(hideProc);
30493         ce = null;
30494         if(el.isVisible()){
30495             esc.disable();
30496             if(noanim !== true && tm.animate){
30497                 el.fadeOut({callback: afterHide});
30498             }else{
30499                 afterHide();
30500             } 
30501         }
30502     };
30503     
30504     var afterHide = function(){
30505         el.hide();
30506         if(removeCls){
30507             el.removeClass(removeCls);
30508             removeCls = null;
30509         }
30510     };
30511     
30512     return {
30513         /**
30514         * @cfg {Number} minWidth
30515         * The minimum width of the quick tip (defaults to 40)
30516         */
30517        minWidth : 40,
30518         /**
30519         * @cfg {Number} maxWidth
30520         * The maximum width of the quick tip (defaults to 300)
30521         */
30522        maxWidth : 300,
30523         /**
30524         * @cfg {Boolean} interceptTitles
30525         * True to automatically use the element's DOM title value if available (defaults to false)
30526         */
30527        interceptTitles : false,
30528         /**
30529         * @cfg {Boolean} trackMouse
30530         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30531         */
30532        trackMouse : false,
30533         /**
30534         * @cfg {Boolean} hideOnClick
30535         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30536         */
30537        hideOnClick : true,
30538         /**
30539         * @cfg {Number} showDelay
30540         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30541         */
30542        showDelay : 500,
30543         /**
30544         * @cfg {Number} hideDelay
30545         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30546         */
30547        hideDelay : 200,
30548         /**
30549         * @cfg {Boolean} autoHide
30550         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30551         * Used in conjunction with hideDelay.
30552         */
30553        autoHide : true,
30554         /**
30555         * @cfg {Boolean}
30556         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30557         * (defaults to true).  Used in conjunction with autoDismissDelay.
30558         */
30559        autoDismiss : true,
30560         /**
30561         * @cfg {Number}
30562         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30563         */
30564        autoDismissDelay : 5000,
30565        /**
30566         * @cfg {Boolean} animate
30567         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30568         */
30569        animate : false,
30570
30571        /**
30572         * @cfg {String} title
30573         * Title text to display (defaults to '').  This can be any valid HTML markup.
30574         */
30575         title: '',
30576        /**
30577         * @cfg {String} text
30578         * Body text to display (defaults to '').  This can be any valid HTML markup.
30579         */
30580         text : '',
30581        /**
30582         * @cfg {String} cls
30583         * A CSS class to apply to the base quick tip element (defaults to '').
30584         */
30585         cls : '',
30586        /**
30587         * @cfg {Number} width
30588         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30589         * minWidth or maxWidth.
30590         */
30591         width : null,
30592
30593     /**
30594      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30595      * or display QuickTips in a page.
30596      */
30597        init : function(){
30598           tm = Roo.QuickTips;
30599           cfg = tm.tagConfig;
30600           if(!inited){
30601               if(!Roo.isReady){ // allow calling of init() before onReady
30602                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30603                   return;
30604               }
30605               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30606               el.fxDefaults = {stopFx: true};
30607               // maximum custom styling
30608               //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>');
30609               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>');              
30610               tipTitle = el.child('h3');
30611               tipTitle.enableDisplayMode("block");
30612               tipBody = el.child('div.x-tip-bd');
30613               tipBodyText = el.child('div.x-tip-bd-inner');
30614               //bdLeft = el.child('div.x-tip-bd-left');
30615               //bdRight = el.child('div.x-tip-bd-right');
30616               close = el.child('div.x-tip-close');
30617               close.enableDisplayMode("block");
30618               close.on("click", hide);
30619               var d = Roo.get(document);
30620               d.on("mousedown", onDown);
30621               d.on("mouseover", onOver);
30622               d.on("mouseout", onOut);
30623               d.on("mousemove", onMove);
30624               esc = d.addKeyListener(27, hide);
30625               esc.disable();
30626               if(Roo.dd.DD){
30627                   dd = el.initDD("default", null, {
30628                       onDrag : function(){
30629                           el.sync();  
30630                       }
30631                   });
30632                   dd.setHandleElId(tipTitle.id);
30633                   dd.lock();
30634               }
30635               inited = true;
30636           }
30637           this.enable(); 
30638        },
30639
30640     /**
30641      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30642      * are supported:
30643      * <pre>
30644 Property    Type                   Description
30645 ----------  ---------------------  ------------------------------------------------------------------------
30646 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30647      * </ul>
30648      * @param {Object} config The config object
30649      */
30650        register : function(config){
30651            var cs = config instanceof Array ? config : arguments;
30652            for(var i = 0, len = cs.length; i < len; i++) {
30653                var c = cs[i];
30654                var target = c.target;
30655                if(target){
30656                    if(target instanceof Array){
30657                        for(var j = 0, jlen = target.length; j < jlen; j++){
30658                            tagEls[target[j]] = c;
30659                        }
30660                    }else{
30661                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30662                    }
30663                }
30664            }
30665        },
30666
30667     /**
30668      * Removes this quick tip from its element and destroys it.
30669      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30670      */
30671        unregister : function(el){
30672            delete tagEls[Roo.id(el)];
30673        },
30674
30675     /**
30676      * Enable this quick tip.
30677      */
30678        enable : function(){
30679            if(inited && disabled){
30680                locks.pop();
30681                if(locks.length < 1){
30682                    disabled = false;
30683                }
30684            }
30685        },
30686
30687     /**
30688      * Disable this quick tip.
30689      */
30690        disable : function(){
30691           disabled = true;
30692           clearTimeout(showProc);
30693           clearTimeout(hideProc);
30694           clearTimeout(dismissProc);
30695           if(ce){
30696               hide(true);
30697           }
30698           locks.push(1);
30699        },
30700
30701     /**
30702      * Returns true if the quick tip is enabled, else false.
30703      */
30704        isEnabled : function(){
30705             return !disabled;
30706        },
30707
30708         // private
30709        tagConfig : {
30710            namespace : "ext",
30711            attribute : "qtip",
30712            width : "width",
30713            target : "target",
30714            title : "qtitle",
30715            hide : "hide",
30716            cls : "qclass"
30717        }
30718    };
30719 }();
30720
30721 // backwards compat
30722 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30723  * Based on:
30724  * Ext JS Library 1.1.1
30725  * Copyright(c) 2006-2007, Ext JS, LLC.
30726  *
30727  * Originally Released Under LGPL - original licence link has changed is not relivant.
30728  *
30729  * Fork - LGPL
30730  * <script type="text/javascript">
30731  */
30732  
30733
30734 /**
30735  * @class Roo.tree.TreePanel
30736  * @extends Roo.data.Tree
30737
30738  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30739  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30740  * @cfg {Boolean} enableDD true to enable drag and drop
30741  * @cfg {Boolean} enableDrag true to enable just drag
30742  * @cfg {Boolean} enableDrop true to enable just drop
30743  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30744  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30745  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30746  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30747  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30748  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30749  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30750  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30751  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30752  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30753  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30754  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30755  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30756  * @cfg {Function} renderer Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30757  * @cfg {Function} rendererTip Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30758  * 
30759  * @constructor
30760  * @param {String/HTMLElement/Element} el The container element
30761  * @param {Object} config
30762  */
30763 Roo.tree.TreePanel = function(el, config){
30764     var root = false;
30765     var loader = false;
30766     if (config.root) {
30767         root = config.root;
30768         delete config.root;
30769     }
30770     if (config.loader) {
30771         loader = config.loader;
30772         delete config.loader;
30773     }
30774     
30775     Roo.apply(this, config);
30776     Roo.tree.TreePanel.superclass.constructor.call(this);
30777     this.el = Roo.get(el);
30778     this.el.addClass('x-tree');
30779     //console.log(root);
30780     if (root) {
30781         this.setRootNode( Roo.factory(root, Roo.tree));
30782     }
30783     if (loader) {
30784         this.loader = Roo.factory(loader, Roo.tree);
30785     }
30786    /**
30787     * Read-only. The id of the container element becomes this TreePanel's id.
30788     */
30789    this.id = this.el.id;
30790    this.addEvents({
30791         /**
30792         * @event beforeload
30793         * Fires before a node is loaded, return false to cancel
30794         * @param {Node} node The node being loaded
30795         */
30796         "beforeload" : true,
30797         /**
30798         * @event load
30799         * Fires when a node is loaded
30800         * @param {Node} node The node that was loaded
30801         */
30802         "load" : true,
30803         /**
30804         * @event textchange
30805         * Fires when the text for a node is changed
30806         * @param {Node} node The node
30807         * @param {String} text The new text
30808         * @param {String} oldText The old text
30809         */
30810         "textchange" : true,
30811         /**
30812         * @event beforeexpand
30813         * Fires before a node is expanded, return false to cancel.
30814         * @param {Node} node The node
30815         * @param {Boolean} deep
30816         * @param {Boolean} anim
30817         */
30818         "beforeexpand" : true,
30819         /**
30820         * @event beforecollapse
30821         * Fires before a node is collapsed, return false to cancel.
30822         * @param {Node} node The node
30823         * @param {Boolean} deep
30824         * @param {Boolean} anim
30825         */
30826         "beforecollapse" : true,
30827         /**
30828         * @event expand
30829         * Fires when a node is expanded
30830         * @param {Node} node The node
30831         */
30832         "expand" : true,
30833         /**
30834         * @event disabledchange
30835         * Fires when the disabled status of a node changes
30836         * @param {Node} node The node
30837         * @param {Boolean} disabled
30838         */
30839         "disabledchange" : true,
30840         /**
30841         * @event collapse
30842         * Fires when a node is collapsed
30843         * @param {Node} node The node
30844         */
30845         "collapse" : true,
30846         /**
30847         * @event beforeclick
30848         * Fires before click processing on a node. Return false to cancel the default action.
30849         * @param {Node} node The node
30850         * @param {Roo.EventObject} e The event object
30851         */
30852         "beforeclick":true,
30853         /**
30854         * @event checkchange
30855         * Fires when a node with a checkbox's checked property changes
30856         * @param {Node} this This node
30857         * @param {Boolean} checked
30858         */
30859         "checkchange":true,
30860         /**
30861         * @event click
30862         * Fires when a node is clicked
30863         * @param {Node} node The node
30864         * @param {Roo.EventObject} e The event object
30865         */
30866         "click":true,
30867         /**
30868         * @event dblclick
30869         * Fires when a node is double clicked
30870         * @param {Node} node The node
30871         * @param {Roo.EventObject} e The event object
30872         */
30873         "dblclick":true,
30874         /**
30875         * @event contextmenu
30876         * Fires when a node is right clicked
30877         * @param {Node} node The node
30878         * @param {Roo.EventObject} e The event object
30879         */
30880         "contextmenu":true,
30881         /**
30882         * @event beforechildrenrendered
30883         * Fires right before the child nodes for a node are rendered
30884         * @param {Node} node The node
30885         */
30886         "beforechildrenrendered":true,
30887        /**
30888              * @event startdrag
30889              * Fires when a node starts being dragged
30890              * @param {Roo.tree.TreePanel} this
30891              * @param {Roo.tree.TreeNode} node
30892              * @param {event} e The raw browser event
30893              */ 
30894             "startdrag" : true,
30895             /**
30896              * @event enddrag
30897              * Fires when a drag operation is complete
30898              * @param {Roo.tree.TreePanel} this
30899              * @param {Roo.tree.TreeNode} node
30900              * @param {event} e The raw browser event
30901              */
30902             "enddrag" : true,
30903             /**
30904              * @event dragdrop
30905              * Fires when a dragged node is dropped on a valid DD target
30906              * @param {Roo.tree.TreePanel} this
30907              * @param {Roo.tree.TreeNode} node
30908              * @param {DD} dd The dd it was dropped on
30909              * @param {event} e The raw browser event
30910              */
30911             "dragdrop" : true,
30912             /**
30913              * @event beforenodedrop
30914              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30915              * passed to handlers has the following properties:<br />
30916              * <ul style="padding:5px;padding-left:16px;">
30917              * <li>tree - The TreePanel</li>
30918              * <li>target - The node being targeted for the drop</li>
30919              * <li>data - The drag data from the drag source</li>
30920              * <li>point - The point of the drop - append, above or below</li>
30921              * <li>source - The drag source</li>
30922              * <li>rawEvent - Raw mouse event</li>
30923              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30924              * to be inserted by setting them on this object.</li>
30925              * <li>cancel - Set this to true to cancel the drop.</li>
30926              * </ul>
30927              * @param {Object} dropEvent
30928              */
30929             "beforenodedrop" : true,
30930             /**
30931              * @event nodedrop
30932              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30933              * passed to handlers has the following properties:<br />
30934              * <ul style="padding:5px;padding-left:16px;">
30935              * <li>tree - The TreePanel</li>
30936              * <li>target - The node being targeted for the drop</li>
30937              * <li>data - The drag data from the drag source</li>
30938              * <li>point - The point of the drop - append, above or below</li>
30939              * <li>source - The drag source</li>
30940              * <li>rawEvent - Raw mouse event</li>
30941              * <li>dropNode - Dropped node(s).</li>
30942              * </ul>
30943              * @param {Object} dropEvent
30944              */
30945             "nodedrop" : true,
30946              /**
30947              * @event nodedragover
30948              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30949              * passed to handlers has the following properties:<br />
30950              * <ul style="padding:5px;padding-left:16px;">
30951              * <li>tree - The TreePanel</li>
30952              * <li>target - The node being targeted for the drop</li>
30953              * <li>data - The drag data from the drag source</li>
30954              * <li>point - The point of the drop - append, above or below</li>
30955              * <li>source - The drag source</li>
30956              * <li>rawEvent - Raw mouse event</li>
30957              * <li>dropNode - Drop node(s) provided by the source.</li>
30958              * <li>cancel - Set this to true to signal drop not allowed.</li>
30959              * </ul>
30960              * @param {Object} dragOverEvent
30961              */
30962             "nodedragover" : true
30963         
30964    });
30965    if(this.singleExpand){
30966        this.on("beforeexpand", this.restrictExpand, this);
30967    }
30968 };
30969 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30970     rootVisible : true,
30971     animate: Roo.enableFx,
30972     lines : true,
30973     enableDD : false,
30974     hlDrop : Roo.enableFx,
30975   
30976     renderer: false,
30977     
30978     rendererTip: false,
30979     // private
30980     restrictExpand : function(node){
30981         var p = node.parentNode;
30982         if(p){
30983             if(p.expandedChild && p.expandedChild.parentNode == p){
30984                 p.expandedChild.collapse();
30985             }
30986             p.expandedChild = node;
30987         }
30988     },
30989
30990     // private override
30991     setRootNode : function(node){
30992         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30993         if(!this.rootVisible){
30994             node.ui = new Roo.tree.RootTreeNodeUI(node);
30995         }
30996         return node;
30997     },
30998
30999     /**
31000      * Returns the container element for this TreePanel
31001      */
31002     getEl : function(){
31003         return this.el;
31004     },
31005
31006     /**
31007      * Returns the default TreeLoader for this TreePanel
31008      */
31009     getLoader : function(){
31010         return this.loader;
31011     },
31012
31013     /**
31014      * Expand all nodes
31015      */
31016     expandAll : function(){
31017         this.root.expand(true);
31018     },
31019
31020     /**
31021      * Collapse all nodes
31022      */
31023     collapseAll : function(){
31024         this.root.collapse(true);
31025     },
31026
31027     /**
31028      * Returns the selection model used by this TreePanel
31029      */
31030     getSelectionModel : function(){
31031         if(!this.selModel){
31032             this.selModel = new Roo.tree.DefaultSelectionModel();
31033         }
31034         return this.selModel;
31035     },
31036
31037     /**
31038      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31039      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31040      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31041      * @return {Array}
31042      */
31043     getChecked : function(a, startNode){
31044         startNode = startNode || this.root;
31045         var r = [];
31046         var f = function(){
31047             if(this.attributes.checked){
31048                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31049             }
31050         }
31051         startNode.cascade(f);
31052         return r;
31053     },
31054
31055     /**
31056      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31057      * @param {String} path
31058      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31059      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31060      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31061      */
31062     expandPath : function(path, attr, callback){
31063         attr = attr || "id";
31064         var keys = path.split(this.pathSeparator);
31065         var curNode = this.root;
31066         if(curNode.attributes[attr] != keys[1]){ // invalid root
31067             if(callback){
31068                 callback(false, null);
31069             }
31070             return;
31071         }
31072         var index = 1;
31073         var f = function(){
31074             if(++index == keys.length){
31075                 if(callback){
31076                     callback(true, curNode);
31077                 }
31078                 return;
31079             }
31080             var c = curNode.findChild(attr, keys[index]);
31081             if(!c){
31082                 if(callback){
31083                     callback(false, curNode);
31084                 }
31085                 return;
31086             }
31087             curNode = c;
31088             c.expand(false, false, f);
31089         };
31090         curNode.expand(false, false, f);
31091     },
31092
31093     /**
31094      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31095      * @param {String} path
31096      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31097      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31098      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31099      */
31100     selectPath : function(path, attr, callback){
31101         attr = attr || "id";
31102         var keys = path.split(this.pathSeparator);
31103         var v = keys.pop();
31104         if(keys.length > 0){
31105             var f = function(success, node){
31106                 if(success && node){
31107                     var n = node.findChild(attr, v);
31108                     if(n){
31109                         n.select();
31110                         if(callback){
31111                             callback(true, n);
31112                         }
31113                     }else if(callback){
31114                         callback(false, n);
31115                     }
31116                 }else{
31117                     if(callback){
31118                         callback(false, n);
31119                     }
31120                 }
31121             };
31122             this.expandPath(keys.join(this.pathSeparator), attr, f);
31123         }else{
31124             this.root.select();
31125             if(callback){
31126                 callback(true, this.root);
31127             }
31128         }
31129     },
31130
31131     getTreeEl : function(){
31132         return this.el;
31133     },
31134
31135     /**
31136      * Trigger rendering of this TreePanel
31137      */
31138     render : function(){
31139         if (this.innerCt) {
31140             return this; // stop it rendering more than once!!
31141         }
31142         
31143         this.innerCt = this.el.createChild({tag:"ul",
31144                cls:"x-tree-root-ct " +
31145                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31146
31147         if(this.containerScroll){
31148             Roo.dd.ScrollManager.register(this.el);
31149         }
31150         if((this.enableDD || this.enableDrop) && !this.dropZone){
31151            /**
31152             * The dropZone used by this tree if drop is enabled
31153             * @type Roo.tree.TreeDropZone
31154             */
31155              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31156                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31157            });
31158         }
31159         if((this.enableDD || this.enableDrag) && !this.dragZone){
31160            /**
31161             * The dragZone used by this tree if drag is enabled
31162             * @type Roo.tree.TreeDragZone
31163             */
31164             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31165                ddGroup: this.ddGroup || "TreeDD",
31166                scroll: this.ddScroll
31167            });
31168         }
31169         this.getSelectionModel().init(this);
31170         if (!this.root) {
31171             console.log("ROOT not set in tree");
31172             return;
31173         }
31174         this.root.render();
31175         if(!this.rootVisible){
31176             this.root.renderChildren();
31177         }
31178         return this;
31179     }
31180 });/*
31181  * Based on:
31182  * Ext JS Library 1.1.1
31183  * Copyright(c) 2006-2007, Ext JS, LLC.
31184  *
31185  * Originally Released Under LGPL - original licence link has changed is not relivant.
31186  *
31187  * Fork - LGPL
31188  * <script type="text/javascript">
31189  */
31190  
31191
31192 /**
31193  * @class Roo.tree.DefaultSelectionModel
31194  * @extends Roo.util.Observable
31195  * The default single selection for a TreePanel.
31196  */
31197 Roo.tree.DefaultSelectionModel = function(){
31198    this.selNode = null;
31199    
31200    this.addEvents({
31201        /**
31202         * @event selectionchange
31203         * Fires when the selected node changes
31204         * @param {DefaultSelectionModel} this
31205         * @param {TreeNode} node the new selection
31206         */
31207        "selectionchange" : true,
31208
31209        /**
31210         * @event beforeselect
31211         * Fires before the selected node changes, return false to cancel the change
31212         * @param {DefaultSelectionModel} this
31213         * @param {TreeNode} node the new selection
31214         * @param {TreeNode} node the old selection
31215         */
31216        "beforeselect" : true
31217    });
31218 };
31219
31220 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31221     init : function(tree){
31222         this.tree = tree;
31223         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31224         tree.on("click", this.onNodeClick, this);
31225     },
31226     
31227     onNodeClick : function(node, e){
31228         if (e.ctrlKey && this.selNode == node)  {
31229             this.unselect(node);
31230             return;
31231         }
31232         this.select(node);
31233     },
31234     
31235     /**
31236      * Select a node.
31237      * @param {TreeNode} node The node to select
31238      * @return {TreeNode} The selected node
31239      */
31240     select : function(node){
31241         var last = this.selNode;
31242         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31243             if(last){
31244                 last.ui.onSelectedChange(false);
31245             }
31246             this.selNode = node;
31247             node.ui.onSelectedChange(true);
31248             this.fireEvent("selectionchange", this, node, last);
31249         }
31250         return node;
31251     },
31252     
31253     /**
31254      * Deselect a node.
31255      * @param {TreeNode} node The node to unselect
31256      */
31257     unselect : function(node){
31258         if(this.selNode == node){
31259             this.clearSelections();
31260         }    
31261     },
31262     
31263     /**
31264      * Clear all selections
31265      */
31266     clearSelections : function(){
31267         var n = this.selNode;
31268         if(n){
31269             n.ui.onSelectedChange(false);
31270             this.selNode = null;
31271             this.fireEvent("selectionchange", this, null);
31272         }
31273         return n;
31274     },
31275     
31276     /**
31277      * Get the selected node
31278      * @return {TreeNode} The selected node
31279      */
31280     getSelectedNode : function(){
31281         return this.selNode;    
31282     },
31283     
31284     /**
31285      * Returns true if the node is selected
31286      * @param {TreeNode} node The node to check
31287      * @return {Boolean}
31288      */
31289     isSelected : function(node){
31290         return this.selNode == node;  
31291     },
31292
31293     /**
31294      * Selects the node above the selected node in the tree, intelligently walking the nodes
31295      * @return TreeNode The new selection
31296      */
31297     selectPrevious : function(){
31298         var s = this.selNode || this.lastSelNode;
31299         if(!s){
31300             return null;
31301         }
31302         var ps = s.previousSibling;
31303         if(ps){
31304             if(!ps.isExpanded() || ps.childNodes.length < 1){
31305                 return this.select(ps);
31306             } else{
31307                 var lc = ps.lastChild;
31308                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31309                     lc = lc.lastChild;
31310                 }
31311                 return this.select(lc);
31312             }
31313         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31314             return this.select(s.parentNode);
31315         }
31316         return null;
31317     },
31318
31319     /**
31320      * Selects the node above the selected node in the tree, intelligently walking the nodes
31321      * @return TreeNode The new selection
31322      */
31323     selectNext : function(){
31324         var s = this.selNode || this.lastSelNode;
31325         if(!s){
31326             return null;
31327         }
31328         if(s.firstChild && s.isExpanded()){
31329              return this.select(s.firstChild);
31330          }else if(s.nextSibling){
31331              return this.select(s.nextSibling);
31332          }else if(s.parentNode){
31333             var newS = null;
31334             s.parentNode.bubble(function(){
31335                 if(this.nextSibling){
31336                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31337                     return false;
31338                 }
31339             });
31340             return newS;
31341          }
31342         return null;
31343     },
31344
31345     onKeyDown : function(e){
31346         var s = this.selNode || this.lastSelNode;
31347         // undesirable, but required
31348         var sm = this;
31349         if(!s){
31350             return;
31351         }
31352         var k = e.getKey();
31353         switch(k){
31354              case e.DOWN:
31355                  e.stopEvent();
31356                  this.selectNext();
31357              break;
31358              case e.UP:
31359                  e.stopEvent();
31360                  this.selectPrevious();
31361              break;
31362              case e.RIGHT:
31363                  e.preventDefault();
31364                  if(s.hasChildNodes()){
31365                      if(!s.isExpanded()){
31366                          s.expand();
31367                      }else if(s.firstChild){
31368                          this.select(s.firstChild, e);
31369                      }
31370                  }
31371              break;
31372              case e.LEFT:
31373                  e.preventDefault();
31374                  if(s.hasChildNodes() && s.isExpanded()){
31375                      s.collapse();
31376                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31377                      this.select(s.parentNode, e);
31378                  }
31379              break;
31380         };
31381     }
31382 });
31383
31384 /**
31385  * @class Roo.tree.MultiSelectionModel
31386  * @extends Roo.util.Observable
31387  * Multi selection for a TreePanel.
31388  */
31389 Roo.tree.MultiSelectionModel = function(){
31390    this.selNodes = [];
31391    this.selMap = {};
31392    this.addEvents({
31393        /**
31394         * @event selectionchange
31395         * Fires when the selected nodes change
31396         * @param {MultiSelectionModel} this
31397         * @param {Array} nodes Array of the selected nodes
31398         */
31399        "selectionchange" : true
31400    });
31401 };
31402
31403 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31404     init : function(tree){
31405         this.tree = tree;
31406         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31407         tree.on("click", this.onNodeClick, this);
31408     },
31409     
31410     onNodeClick : function(node, e){
31411         this.select(node, e, e.ctrlKey);
31412     },
31413     
31414     /**
31415      * Select a node.
31416      * @param {TreeNode} node The node to select
31417      * @param {EventObject} e (optional) An event associated with the selection
31418      * @param {Boolean} keepExisting True to retain existing selections
31419      * @return {TreeNode} The selected node
31420      */
31421     select : function(node, e, keepExisting){
31422         if(keepExisting !== true){
31423             this.clearSelections(true);
31424         }
31425         if(this.isSelected(node)){
31426             this.lastSelNode = node;
31427             return node;
31428         }
31429         this.selNodes.push(node);
31430         this.selMap[node.id] = node;
31431         this.lastSelNode = node;
31432         node.ui.onSelectedChange(true);
31433         this.fireEvent("selectionchange", this, this.selNodes);
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.selMap[node.id]){
31443             node.ui.onSelectedChange(false);
31444             var sn = this.selNodes;
31445             var index = -1;
31446             if(sn.indexOf){
31447                 index = sn.indexOf(node);
31448             }else{
31449                 for(var i = 0, len = sn.length; i < len; i++){
31450                     if(sn[i] == node){
31451                         index = i;
31452                         break;
31453                     }
31454                 }
31455             }
31456             if(index != -1){
31457                 this.selNodes.splice(index, 1);
31458             }
31459             delete this.selMap[node.id];
31460             this.fireEvent("selectionchange", this, this.selNodes);
31461         }
31462     },
31463     
31464     /**
31465      * Clear all selections
31466      */
31467     clearSelections : function(suppressEvent){
31468         var sn = this.selNodes;
31469         if(sn.length > 0){
31470             for(var i = 0, len = sn.length; i < len; i++){
31471                 sn[i].ui.onSelectedChange(false);
31472             }
31473             this.selNodes = [];
31474             this.selMap = {};
31475             if(suppressEvent !== true){
31476                 this.fireEvent("selectionchange", this, this.selNodes);
31477             }
31478         }
31479     },
31480     
31481     /**
31482      * Returns true if the node is selected
31483      * @param {TreeNode} node The node to check
31484      * @return {Boolean}
31485      */
31486     isSelected : function(node){
31487         return this.selMap[node.id] ? true : false;  
31488     },
31489     
31490     /**
31491      * Returns an array of the selected nodes
31492      * @return {Array}
31493      */
31494     getSelectedNodes : function(){
31495         return this.selNodes;    
31496     },
31497
31498     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31499
31500     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31501
31502     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31503 });/*
31504  * Based on:
31505  * Ext JS Library 1.1.1
31506  * Copyright(c) 2006-2007, Ext JS, LLC.
31507  *
31508  * Originally Released Under LGPL - original licence link has changed is not relivant.
31509  *
31510  * Fork - LGPL
31511  * <script type="text/javascript">
31512  */
31513  
31514 /**
31515  * @class Roo.tree.TreeNode
31516  * @extends Roo.data.Node
31517  * @cfg {String} text The text for this node
31518  * @cfg {Boolean} expanded true to start the node expanded
31519  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31520  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31521  * @cfg {Boolean} disabled true to start the node disabled
31522  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31523  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31524  * @cfg {String} cls A css class to be added to the node
31525  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31526  * @cfg {String} href URL of the link used for the node (defaults to #)
31527  * @cfg {String} hrefTarget target frame for the link
31528  * @cfg {String} qtip An Ext QuickTip for the node
31529  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31530  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31531  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31532  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31533  * (defaults to undefined with no checkbox rendered)
31534  * @constructor
31535  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31536  */
31537 Roo.tree.TreeNode = function(attributes){
31538     attributes = attributes || {};
31539     if(typeof attributes == "string"){
31540         attributes = {text: attributes};
31541     }
31542     this.childrenRendered = false;
31543     this.rendered = false;
31544     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31545     this.expanded = attributes.expanded === true;
31546     this.isTarget = attributes.isTarget !== false;
31547     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31548     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31549
31550     /**
31551      * Read-only. The text for this node. To change it use setText().
31552      * @type String
31553      */
31554     this.text = attributes.text;
31555     /**
31556      * True if this node is disabled.
31557      * @type Boolean
31558      */
31559     this.disabled = attributes.disabled === true;
31560
31561     this.addEvents({
31562         /**
31563         * @event textchange
31564         * Fires when the text for this node is changed
31565         * @param {Node} this This node
31566         * @param {String} text The new text
31567         * @param {String} oldText The old text
31568         */
31569         "textchange" : true,
31570         /**
31571         * @event beforeexpand
31572         * Fires before this node is expanded, return false to cancel.
31573         * @param {Node} this This node
31574         * @param {Boolean} deep
31575         * @param {Boolean} anim
31576         */
31577         "beforeexpand" : true,
31578         /**
31579         * @event beforecollapse
31580         * Fires before this node is collapsed, return false to cancel.
31581         * @param {Node} this This node
31582         * @param {Boolean} deep
31583         * @param {Boolean} anim
31584         */
31585         "beforecollapse" : true,
31586         /**
31587         * @event expand
31588         * Fires when this node is expanded
31589         * @param {Node} this This node
31590         */
31591         "expand" : true,
31592         /**
31593         * @event disabledchange
31594         * Fires when the disabled status of this node changes
31595         * @param {Node} this This node
31596         * @param {Boolean} disabled
31597         */
31598         "disabledchange" : true,
31599         /**
31600         * @event collapse
31601         * Fires when this node is collapsed
31602         * @param {Node} this This node
31603         */
31604         "collapse" : true,
31605         /**
31606         * @event beforeclick
31607         * Fires before click processing. Return false to cancel the default action.
31608         * @param {Node} this This node
31609         * @param {Roo.EventObject} e The event object
31610         */
31611         "beforeclick":true,
31612         /**
31613         * @event checkchange
31614         * Fires when a node with a checkbox's checked property changes
31615         * @param {Node} this This node
31616         * @param {Boolean} checked
31617         */
31618         "checkchange":true,
31619         /**
31620         * @event click
31621         * Fires when this node is clicked
31622         * @param {Node} this This node
31623         * @param {Roo.EventObject} e The event object
31624         */
31625         "click":true,
31626         /**
31627         * @event dblclick
31628         * Fires when this node is double clicked
31629         * @param {Node} this This node
31630         * @param {Roo.EventObject} e The event object
31631         */
31632         "dblclick":true,
31633         /**
31634         * @event contextmenu
31635         * Fires when this node is right clicked
31636         * @param {Node} this This node
31637         * @param {Roo.EventObject} e The event object
31638         */
31639         "contextmenu":true,
31640         /**
31641         * @event beforechildrenrendered
31642         * Fires right before the child nodes for this node are rendered
31643         * @param {Node} this This node
31644         */
31645         "beforechildrenrendered":true
31646     });
31647
31648     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31649
31650     /**
31651      * Read-only. The UI for this node
31652      * @type TreeNodeUI
31653      */
31654     this.ui = new uiClass(this);
31655 };
31656 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31657     preventHScroll: true,
31658     /**
31659      * Returns true if this node is expanded
31660      * @return {Boolean}
31661      */
31662     isExpanded : function(){
31663         return this.expanded;
31664     },
31665
31666     /**
31667      * Returns the UI object for this node
31668      * @return {TreeNodeUI}
31669      */
31670     getUI : function(){
31671         return this.ui;
31672     },
31673
31674     // private override
31675     setFirstChild : function(node){
31676         var of = this.firstChild;
31677         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31678         if(this.childrenRendered && of && node != of){
31679             of.renderIndent(true, true);
31680         }
31681         if(this.rendered){
31682             this.renderIndent(true, true);
31683         }
31684     },
31685
31686     // private override
31687     setLastChild : function(node){
31688         var ol = this.lastChild;
31689         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31690         if(this.childrenRendered && ol && node != ol){
31691             ol.renderIndent(true, true);
31692         }
31693         if(this.rendered){
31694             this.renderIndent(true, true);
31695         }
31696     },
31697
31698     // these methods are overridden to provide lazy rendering support
31699     // private override
31700     appendChild : function(){
31701         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31702         if(node && this.childrenRendered){
31703             node.render();
31704         }
31705         this.ui.updateExpandIcon();
31706         return node;
31707     },
31708
31709     // private override
31710     removeChild : function(node){
31711         this.ownerTree.getSelectionModel().unselect(node);
31712         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31713         // if it's been rendered remove dom node
31714         if(this.childrenRendered){
31715             node.ui.remove();
31716         }
31717         if(this.childNodes.length < 1){
31718             this.collapse(false, false);
31719         }else{
31720             this.ui.updateExpandIcon();
31721         }
31722         if(!this.firstChild) {
31723             this.childrenRendered = false;
31724         }
31725         return node;
31726     },
31727
31728     // private override
31729     insertBefore : function(node, refNode){
31730         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31731         if(newNode && refNode && this.childrenRendered){
31732             node.render();
31733         }
31734         this.ui.updateExpandIcon();
31735         return newNode;
31736     },
31737
31738     /**
31739      * Sets the text for this node
31740      * @param {String} text
31741      */
31742     setText : function(text){
31743         var oldText = this.text;
31744         this.text = text;
31745         this.attributes.text = text;
31746         if(this.rendered){ // event without subscribing
31747             this.ui.onTextChange(this, text, oldText);
31748         }
31749         this.fireEvent("textchange", this, text, oldText);
31750     },
31751
31752     /**
31753      * Triggers selection of this node
31754      */
31755     select : function(){
31756         this.getOwnerTree().getSelectionModel().select(this);
31757     },
31758
31759     /**
31760      * Triggers deselection of this node
31761      */
31762     unselect : function(){
31763         this.getOwnerTree().getSelectionModel().unselect(this);
31764     },
31765
31766     /**
31767      * Returns true if this node is selected
31768      * @return {Boolean}
31769      */
31770     isSelected : function(){
31771         return this.getOwnerTree().getSelectionModel().isSelected(this);
31772     },
31773
31774     /**
31775      * Expand this node.
31776      * @param {Boolean} deep (optional) True to expand all children as well
31777      * @param {Boolean} anim (optional) false to cancel the default animation
31778      * @param {Function} callback (optional) A callback to be called when
31779      * expanding this node completes (does not wait for deep expand to complete).
31780      * Called with 1 parameter, this node.
31781      */
31782     expand : function(deep, anim, callback){
31783         if(!this.expanded){
31784             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31785                 return;
31786             }
31787             if(!this.childrenRendered){
31788                 this.renderChildren();
31789             }
31790             this.expanded = true;
31791             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31792                 this.ui.animExpand(function(){
31793                     this.fireEvent("expand", this);
31794                     if(typeof callback == "function"){
31795                         callback(this);
31796                     }
31797                     if(deep === true){
31798                         this.expandChildNodes(true);
31799                     }
31800                 }.createDelegate(this));
31801                 return;
31802             }else{
31803                 this.ui.expand();
31804                 this.fireEvent("expand", this);
31805                 if(typeof callback == "function"){
31806                     callback(this);
31807                 }
31808             }
31809         }else{
31810            if(typeof callback == "function"){
31811                callback(this);
31812            }
31813         }
31814         if(deep === true){
31815             this.expandChildNodes(true);
31816         }
31817     },
31818
31819     isHiddenRoot : function(){
31820         return this.isRoot && !this.getOwnerTree().rootVisible;
31821     },
31822
31823     /**
31824      * Collapse this node.
31825      * @param {Boolean} deep (optional) True to collapse all children as well
31826      * @param {Boolean} anim (optional) false to cancel the default animation
31827      */
31828     collapse : function(deep, anim){
31829         if(this.expanded && !this.isHiddenRoot()){
31830             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31831                 return;
31832             }
31833             this.expanded = false;
31834             if((this.getOwnerTree().animate && anim !== false) || anim){
31835                 this.ui.animCollapse(function(){
31836                     this.fireEvent("collapse", this);
31837                     if(deep === true){
31838                         this.collapseChildNodes(true);
31839                     }
31840                 }.createDelegate(this));
31841                 return;
31842             }else{
31843                 this.ui.collapse();
31844                 this.fireEvent("collapse", this);
31845             }
31846         }
31847         if(deep === true){
31848             var cs = this.childNodes;
31849             for(var i = 0, len = cs.length; i < len; i++) {
31850                 cs[i].collapse(true, false);
31851             }
31852         }
31853     },
31854
31855     // private
31856     delayedExpand : function(delay){
31857         if(!this.expandProcId){
31858             this.expandProcId = this.expand.defer(delay, this);
31859         }
31860     },
31861
31862     // private
31863     cancelExpand : function(){
31864         if(this.expandProcId){
31865             clearTimeout(this.expandProcId);
31866         }
31867         this.expandProcId = false;
31868     },
31869
31870     /**
31871      * Toggles expanded/collapsed state of the node
31872      */
31873     toggle : function(){
31874         if(this.expanded){
31875             this.collapse();
31876         }else{
31877             this.expand();
31878         }
31879     },
31880
31881     /**
31882      * Ensures all parent nodes are expanded
31883      */
31884     ensureVisible : function(callback){
31885         var tree = this.getOwnerTree();
31886         tree.expandPath(this.parentNode.getPath(), false, function(){
31887             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31888             Roo.callback(callback);
31889         }.createDelegate(this));
31890     },
31891
31892     /**
31893      * Expand all child nodes
31894      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31895      */
31896     expandChildNodes : function(deep){
31897         var cs = this.childNodes;
31898         for(var i = 0, len = cs.length; i < len; i++) {
31899                 cs[i].expand(deep);
31900         }
31901     },
31902
31903     /**
31904      * Collapse all child nodes
31905      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31906      */
31907     collapseChildNodes : function(deep){
31908         var cs = this.childNodes;
31909         for(var i = 0, len = cs.length; i < len; i++) {
31910                 cs[i].collapse(deep);
31911         }
31912     },
31913
31914     /**
31915      * Disables this node
31916      */
31917     disable : function(){
31918         this.disabled = true;
31919         this.unselect();
31920         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31921             this.ui.onDisableChange(this, true);
31922         }
31923         this.fireEvent("disabledchange", this, true);
31924     },
31925
31926     /**
31927      * Enables this node
31928      */
31929     enable : function(){
31930         this.disabled = false;
31931         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31932             this.ui.onDisableChange(this, false);
31933         }
31934         this.fireEvent("disabledchange", this, false);
31935     },
31936
31937     // private
31938     renderChildren : function(suppressEvent){
31939         if(suppressEvent !== false){
31940             this.fireEvent("beforechildrenrendered", this);
31941         }
31942         var cs = this.childNodes;
31943         for(var i = 0, len = cs.length; i < len; i++){
31944             cs[i].render(true);
31945         }
31946         this.childrenRendered = true;
31947     },
31948
31949     // private
31950     sort : function(fn, scope){
31951         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31952         if(this.childrenRendered){
31953             var cs = this.childNodes;
31954             for(var i = 0, len = cs.length; i < len; i++){
31955                 cs[i].render(true);
31956             }
31957         }
31958     },
31959
31960     // private
31961     render : function(bulkRender){
31962         this.ui.render(bulkRender);
31963         if(!this.rendered){
31964             this.rendered = true;
31965             if(this.expanded){
31966                 this.expanded = false;
31967                 this.expand(false, false);
31968             }
31969         }
31970     },
31971
31972     // private
31973     renderIndent : function(deep, refresh){
31974         if(refresh){
31975             this.ui.childIndent = null;
31976         }
31977         this.ui.renderIndent();
31978         if(deep === true && this.childrenRendered){
31979             var cs = this.childNodes;
31980             for(var i = 0, len = cs.length; i < len; i++){
31981                 cs[i].renderIndent(true, refresh);
31982             }
31983         }
31984     }
31985 });/*
31986  * Based on:
31987  * Ext JS Library 1.1.1
31988  * Copyright(c) 2006-2007, Ext JS, LLC.
31989  *
31990  * Originally Released Under LGPL - original licence link has changed is not relivant.
31991  *
31992  * Fork - LGPL
31993  * <script type="text/javascript">
31994  */
31995  
31996 /**
31997  * @class Roo.tree.AsyncTreeNode
31998  * @extends Roo.tree.TreeNode
31999  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32000  * @constructor
32001  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32002  */
32003  Roo.tree.AsyncTreeNode = function(config){
32004     this.loaded = false;
32005     this.loading = false;
32006     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32007     /**
32008     * @event beforeload
32009     * Fires before this node is loaded, return false to cancel
32010     * @param {Node} this This node
32011     */
32012     this.addEvents({'beforeload':true, 'load': true});
32013     /**
32014     * @event load
32015     * Fires when this node is loaded
32016     * @param {Node} this This node
32017     */
32018     /**
32019      * The loader used by this node (defaults to using the tree's defined loader)
32020      * @type TreeLoader
32021      * @property loader
32022      */
32023 };
32024 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32025     expand : function(deep, anim, callback){
32026         if(this.loading){ // if an async load is already running, waiting til it's done
32027             var timer;
32028             var f = function(){
32029                 if(!this.loading){ // done loading
32030                     clearInterval(timer);
32031                     this.expand(deep, anim, callback);
32032                 }
32033             }.createDelegate(this);
32034             timer = setInterval(f, 200);
32035             return;
32036         }
32037         if(!this.loaded){
32038             if(this.fireEvent("beforeload", this) === false){
32039                 return;
32040             }
32041             this.loading = true;
32042             this.ui.beforeLoad(this);
32043             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32044             if(loader){
32045                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32046                 return;
32047             }
32048         }
32049         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32050     },
32051     
32052     /**
32053      * Returns true if this node is currently loading
32054      * @return {Boolean}
32055      */
32056     isLoading : function(){
32057         return this.loading;  
32058     },
32059     
32060     loadComplete : function(deep, anim, callback){
32061         this.loading = false;
32062         this.loaded = true;
32063         this.ui.afterLoad(this);
32064         this.fireEvent("load", this);
32065         this.expand(deep, anim, callback);
32066     },
32067     
32068     /**
32069      * Returns true if this node has been loaded
32070      * @return {Boolean}
32071      */
32072     isLoaded : function(){
32073         return this.loaded;
32074     },
32075     
32076     hasChildNodes : function(){
32077         if(!this.isLeaf() && !this.loaded){
32078             return true;
32079         }else{
32080             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32081         }
32082     },
32083
32084     /**
32085      * Trigger a reload for this node
32086      * @param {Function} callback
32087      */
32088     reload : function(callback){
32089         this.collapse(false, false);
32090         while(this.firstChild){
32091             this.removeChild(this.firstChild);
32092         }
32093         this.childrenRendered = false;
32094         this.loaded = false;
32095         if(this.isHiddenRoot()){
32096             this.expanded = false;
32097         }
32098         this.expand(false, false, callback);
32099     }
32100 });/*
32101  * Based on:
32102  * Ext JS Library 1.1.1
32103  * Copyright(c) 2006-2007, Ext JS, LLC.
32104  *
32105  * Originally Released Under LGPL - original licence link has changed is not relivant.
32106  *
32107  * Fork - LGPL
32108  * <script type="text/javascript">
32109  */
32110  
32111 /**
32112  * @class Roo.tree.TreeNodeUI
32113  * @constructor
32114  * @param {Object} node The node to render
32115  * The TreeNode UI implementation is separate from the
32116  * tree implementation. Unless you are customizing the tree UI,
32117  * you should never have to use this directly.
32118  */
32119 Roo.tree.TreeNodeUI = function(node){
32120     this.node = node;
32121     this.rendered = false;
32122     this.animating = false;
32123     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32124 };
32125
32126 Roo.tree.TreeNodeUI.prototype = {
32127     removeChild : function(node){
32128         if(this.rendered){
32129             this.ctNode.removeChild(node.ui.getEl());
32130         }
32131     },
32132
32133     beforeLoad : function(){
32134          this.addClass("x-tree-node-loading");
32135     },
32136
32137     afterLoad : function(){
32138          this.removeClass("x-tree-node-loading");
32139     },
32140
32141     onTextChange : function(node, text, oldText){
32142         if(this.rendered){
32143             this.textNode.innerHTML = text;
32144         }
32145     },
32146
32147     onDisableChange : function(node, state){
32148         this.disabled = state;
32149         if(state){
32150             this.addClass("x-tree-node-disabled");
32151         }else{
32152             this.removeClass("x-tree-node-disabled");
32153         }
32154     },
32155
32156     onSelectedChange : function(state){
32157         if(state){
32158             this.focus();
32159             this.addClass("x-tree-selected");
32160         }else{
32161             //this.blur();
32162             this.removeClass("x-tree-selected");
32163         }
32164     },
32165
32166     onMove : function(tree, node, oldParent, newParent, index, refNode){
32167         this.childIndent = null;
32168         if(this.rendered){
32169             var targetNode = newParent.ui.getContainer();
32170             if(!targetNode){//target not rendered
32171                 this.holder = document.createElement("div");
32172                 this.holder.appendChild(this.wrap);
32173                 return;
32174             }
32175             var insertBefore = refNode ? refNode.ui.getEl() : null;
32176             if(insertBefore){
32177                 targetNode.insertBefore(this.wrap, insertBefore);
32178             }else{
32179                 targetNode.appendChild(this.wrap);
32180             }
32181             this.node.renderIndent(true);
32182         }
32183     },
32184
32185     addClass : function(cls){
32186         if(this.elNode){
32187             Roo.fly(this.elNode).addClass(cls);
32188         }
32189     },
32190
32191     removeClass : function(cls){
32192         if(this.elNode){
32193             Roo.fly(this.elNode).removeClass(cls);
32194         }
32195     },
32196
32197     remove : function(){
32198         if(this.rendered){
32199             this.holder = document.createElement("div");
32200             this.holder.appendChild(this.wrap);
32201         }
32202     },
32203
32204     fireEvent : function(){
32205         return this.node.fireEvent.apply(this.node, arguments);
32206     },
32207
32208     initEvents : function(){
32209         this.node.on("move", this.onMove, this);
32210         var E = Roo.EventManager;
32211         var a = this.anchor;
32212
32213         var el = Roo.fly(a, '_treeui');
32214
32215         if(Roo.isOpera){ // opera render bug ignores the CSS
32216             el.setStyle("text-decoration", "none");
32217         }
32218
32219         el.on("click", this.onClick, this);
32220         el.on("dblclick", this.onDblClick, this);
32221
32222         if(this.checkbox){
32223             Roo.EventManager.on(this.checkbox,
32224                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32225         }
32226
32227         el.on("contextmenu", this.onContextMenu, this);
32228
32229         var icon = Roo.fly(this.iconNode);
32230         icon.on("click", this.onClick, this);
32231         icon.on("dblclick", this.onDblClick, this);
32232         icon.on("contextmenu", this.onContextMenu, this);
32233         E.on(this.ecNode, "click", this.ecClick, this, true);
32234
32235         if(this.node.disabled){
32236             this.addClass("x-tree-node-disabled");
32237         }
32238         if(this.node.hidden){
32239             this.addClass("x-tree-node-disabled");
32240         }
32241         var ot = this.node.getOwnerTree();
32242         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32243         if(dd && (!this.node.isRoot || ot.rootVisible)){
32244             Roo.dd.Registry.register(this.elNode, {
32245                 node: this.node,
32246                 handles: this.getDDHandles(),
32247                 isHandle: false
32248             });
32249         }
32250     },
32251
32252     getDDHandles : function(){
32253         return [this.iconNode, this.textNode];
32254     },
32255
32256     hide : function(){
32257         if(this.rendered){
32258             this.wrap.style.display = "none";
32259         }
32260     },
32261
32262     show : function(){
32263         if(this.rendered){
32264             this.wrap.style.display = "";
32265         }
32266     },
32267
32268     onContextMenu : function(e){
32269         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32270             e.preventDefault();
32271             this.focus();
32272             this.fireEvent("contextmenu", this.node, e);
32273         }
32274     },
32275
32276     onClick : function(e){
32277         if(this.dropping){
32278             e.stopEvent();
32279             return;
32280         }
32281         if(this.fireEvent("beforeclick", this.node, e) !== false){
32282             if(!this.disabled && this.node.attributes.href){
32283                 this.fireEvent("click", this.node, e);
32284                 return;
32285             }
32286             e.preventDefault();
32287             if(this.disabled){
32288                 return;
32289             }
32290
32291             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32292                 this.node.toggle();
32293             }
32294
32295             this.fireEvent("click", this.node, e);
32296         }else{
32297             e.stopEvent();
32298         }
32299     },
32300
32301     onDblClick : function(e){
32302         e.preventDefault();
32303         if(this.disabled){
32304             return;
32305         }
32306         if(this.checkbox){
32307             this.toggleCheck();
32308         }
32309         if(!this.animating && this.node.hasChildNodes()){
32310             this.node.toggle();
32311         }
32312         this.fireEvent("dblclick", this.node, e);
32313     },
32314
32315     onCheckChange : function(){
32316         var checked = this.checkbox.checked;
32317         this.node.attributes.checked = checked;
32318         this.fireEvent('checkchange', this.node, checked);
32319     },
32320
32321     ecClick : function(e){
32322         if(!this.animating && this.node.hasChildNodes()){
32323             this.node.toggle();
32324         }
32325     },
32326
32327     startDrop : function(){
32328         this.dropping = true;
32329     },
32330
32331     // delayed drop so the click event doesn't get fired on a drop
32332     endDrop : function(){
32333        setTimeout(function(){
32334            this.dropping = false;
32335        }.createDelegate(this), 50);
32336     },
32337
32338     expand : function(){
32339         this.updateExpandIcon();
32340         this.ctNode.style.display = "";
32341     },
32342
32343     focus : function(){
32344         if(!this.node.preventHScroll){
32345             try{this.anchor.focus();
32346             }catch(e){}
32347         }else if(!Roo.isIE){
32348             try{
32349                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32350                 var l = noscroll.scrollLeft;
32351                 this.anchor.focus();
32352                 noscroll.scrollLeft = l;
32353             }catch(e){}
32354         }
32355     },
32356
32357     toggleCheck : function(value){
32358         var cb = this.checkbox;
32359         if(cb){
32360             cb.checked = (value === undefined ? !cb.checked : value);
32361         }
32362     },
32363
32364     blur : function(){
32365         try{
32366             this.anchor.blur();
32367         }catch(e){}
32368     },
32369
32370     animExpand : function(callback){
32371         var ct = Roo.get(this.ctNode);
32372         ct.stopFx();
32373         if(!this.node.hasChildNodes()){
32374             this.updateExpandIcon();
32375             this.ctNode.style.display = "";
32376             Roo.callback(callback);
32377             return;
32378         }
32379         this.animating = true;
32380         this.updateExpandIcon();
32381
32382         ct.slideIn('t', {
32383            callback : function(){
32384                this.animating = false;
32385                Roo.callback(callback);
32386             },
32387             scope: this,
32388             duration: this.node.ownerTree.duration || .25
32389         });
32390     },
32391
32392     highlight : function(){
32393         var tree = this.node.getOwnerTree();
32394         Roo.fly(this.wrap).highlight(
32395             tree.hlColor || "C3DAF9",
32396             {endColor: tree.hlBaseColor}
32397         );
32398     },
32399
32400     collapse : function(){
32401         this.updateExpandIcon();
32402         this.ctNode.style.display = "none";
32403     },
32404
32405     animCollapse : function(callback){
32406         var ct = Roo.get(this.ctNode);
32407         ct.enableDisplayMode('block');
32408         ct.stopFx();
32409
32410         this.animating = true;
32411         this.updateExpandIcon();
32412
32413         ct.slideOut('t', {
32414             callback : function(){
32415                this.animating = false;
32416                Roo.callback(callback);
32417             },
32418             scope: this,
32419             duration: this.node.ownerTree.duration || .25
32420         });
32421     },
32422
32423     getContainer : function(){
32424         return this.ctNode;
32425     },
32426
32427     getEl : function(){
32428         return this.wrap;
32429     },
32430
32431     appendDDGhost : function(ghostNode){
32432         ghostNode.appendChild(this.elNode.cloneNode(true));
32433     },
32434
32435     getDDRepairXY : function(){
32436         return Roo.lib.Dom.getXY(this.iconNode);
32437     },
32438
32439     onRender : function(){
32440         this.render();
32441     },
32442
32443     render : function(bulkRender){
32444         var n = this.node, a = n.attributes;
32445         var targetNode = n.parentNode ?
32446               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32447
32448         if(!this.rendered){
32449             this.rendered = true;
32450
32451             this.renderElements(n, a, targetNode, bulkRender);
32452
32453             if(a.qtip){
32454                if(this.textNode.setAttributeNS){
32455                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32456                    if(a.qtipTitle){
32457                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32458                    }
32459                }else{
32460                    this.textNode.setAttribute("ext:qtip", a.qtip);
32461                    if(a.qtipTitle){
32462                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32463                    }
32464                }
32465             }else if(a.qtipCfg){
32466                 a.qtipCfg.target = Roo.id(this.textNode);
32467                 Roo.QuickTips.register(a.qtipCfg);
32468             }
32469             this.initEvents();
32470             if(!this.node.expanded){
32471                 this.updateExpandIcon();
32472             }
32473         }else{
32474             if(bulkRender === true) {
32475                 targetNode.appendChild(this.wrap);
32476             }
32477         }
32478     },
32479
32480     renderElements : function(n, a, targetNode, bulkRender){
32481         // add some indent caching, this helps performance when rendering a large tree
32482         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32483         var t = n.getOwnerTree();
32484         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32485         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32486         var cb = typeof a.checked == 'boolean';
32487         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32488         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32489             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32490             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32491             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32492             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32493             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32494              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32495                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32496             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32497             "</li>"];
32498
32499         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32500             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32501                                 n.nextSibling.ui.getEl(), buf.join(""));
32502         }else{
32503             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32504         }
32505
32506         this.elNode = this.wrap.childNodes[0];
32507         this.ctNode = this.wrap.childNodes[1];
32508         var cs = this.elNode.childNodes;
32509         this.indentNode = cs[0];
32510         this.ecNode = cs[1];
32511         this.iconNode = cs[2];
32512         var index = 3;
32513         if(cb){
32514             this.checkbox = cs[3];
32515             index++;
32516         }
32517         this.anchor = cs[index];
32518         this.textNode = cs[index].firstChild;
32519     },
32520
32521     getAnchor : function(){
32522         return this.anchor;
32523     },
32524
32525     getTextEl : function(){
32526         return this.textNode;
32527     },
32528
32529     getIconEl : function(){
32530         return this.iconNode;
32531     },
32532
32533     isChecked : function(){
32534         return this.checkbox ? this.checkbox.checked : false;
32535     },
32536
32537     updateExpandIcon : function(){
32538         if(this.rendered){
32539             var n = this.node, c1, c2;
32540             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32541             var hasChild = n.hasChildNodes();
32542             if(hasChild){
32543                 if(n.expanded){
32544                     cls += "-minus";
32545                     c1 = "x-tree-node-collapsed";
32546                     c2 = "x-tree-node-expanded";
32547                 }else{
32548                     cls += "-plus";
32549                     c1 = "x-tree-node-expanded";
32550                     c2 = "x-tree-node-collapsed";
32551                 }
32552                 if(this.wasLeaf){
32553                     this.removeClass("x-tree-node-leaf");
32554                     this.wasLeaf = false;
32555                 }
32556                 if(this.c1 != c1 || this.c2 != c2){
32557                     Roo.fly(this.elNode).replaceClass(c1, c2);
32558                     this.c1 = c1; this.c2 = c2;
32559                 }
32560             }else{
32561                 if(!this.wasLeaf){
32562                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32563                     delete this.c1;
32564                     delete this.c2;
32565                     this.wasLeaf = true;
32566                 }
32567             }
32568             var ecc = "x-tree-ec-icon "+cls;
32569             if(this.ecc != ecc){
32570                 this.ecNode.className = ecc;
32571                 this.ecc = ecc;
32572             }
32573         }
32574     },
32575
32576     getChildIndent : function(){
32577         if(!this.childIndent){
32578             var buf = [];
32579             var p = this.node;
32580             while(p){
32581                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32582                     if(!p.isLast()) {
32583                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32584                     } else {
32585                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32586                     }
32587                 }
32588                 p = p.parentNode;
32589             }
32590             this.childIndent = buf.join("");
32591         }
32592         return this.childIndent;
32593     },
32594
32595     renderIndent : function(){
32596         if(this.rendered){
32597             var indent = "";
32598             var p = this.node.parentNode;
32599             if(p){
32600                 indent = p.ui.getChildIndent();
32601             }
32602             if(this.indentMarkup != indent){ // don't rerender if not required
32603                 this.indentNode.innerHTML = indent;
32604                 this.indentMarkup = indent;
32605             }
32606             this.updateExpandIcon();
32607         }
32608     }
32609 };
32610
32611 Roo.tree.RootTreeNodeUI = function(){
32612     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32613 };
32614 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32615     render : function(){
32616         if(!this.rendered){
32617             var targetNode = this.node.ownerTree.innerCt.dom;
32618             this.node.expanded = true;
32619             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32620             this.wrap = this.ctNode = targetNode.firstChild;
32621         }
32622     },
32623     collapse : function(){
32624     },
32625     expand : function(){
32626     }
32627 });/*
32628  * Based on:
32629  * Ext JS Library 1.1.1
32630  * Copyright(c) 2006-2007, Ext JS, LLC.
32631  *
32632  * Originally Released Under LGPL - original licence link has changed is not relivant.
32633  *
32634  * Fork - LGPL
32635  * <script type="text/javascript">
32636  */
32637 /**
32638  * @class Roo.tree.TreeLoader
32639  * @extends Roo.util.Observable
32640  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32641  * nodes from a specified URL. The response must be a javascript Array definition
32642  * who's elements are node definition objects. eg:
32643  * <pre><code>
32644    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32645     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32646 </code></pre>
32647  * <br><br>
32648  * A server request is sent, and child nodes are loaded only when a node is expanded.
32649  * The loading node's id is passed to the server under the parameter name "node" to
32650  * enable the server to produce the correct child nodes.
32651  * <br><br>
32652  * To pass extra parameters, an event handler may be attached to the "beforeload"
32653  * event, and the parameters specified in the TreeLoader's baseParams property:
32654  * <pre><code>
32655     myTreeLoader.on("beforeload", function(treeLoader, node) {
32656         this.baseParams.category = node.attributes.category;
32657     }, this);
32658 </code></pre><
32659  * This would pass an HTTP parameter called "category" to the server containing
32660  * the value of the Node's "category" attribute.
32661  * @constructor
32662  * Creates a new Treeloader.
32663  * @param {Object} config A config object containing config properties.
32664  */
32665 Roo.tree.TreeLoader = function(config){
32666     this.baseParams = {};
32667     this.requestMethod = "POST";
32668     Roo.apply(this, config);
32669
32670     this.addEvents({
32671     
32672         /**
32673          * @event beforeload
32674          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32675          * @param {Object} This TreeLoader object.
32676          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32677          * @param {Object} callback The callback function specified in the {@link #load} call.
32678          */
32679         beforeload : true,
32680         /**
32681          * @event load
32682          * Fires when the node has been successfuly loaded.
32683          * @param {Object} This TreeLoader object.
32684          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32685          * @param {Object} response The response object containing the data from the server.
32686          */
32687         load : true,
32688         /**
32689          * @event loadexception
32690          * Fires if the network request failed.
32691          * @param {Object} This TreeLoader object.
32692          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32693          * @param {Object} response The response object containing the data from the server.
32694          */
32695         loadexception : true,
32696         /**
32697          * @event create
32698          * Fires before a node is created, enabling you to return custom Node types 
32699          * @param {Object} This TreeLoader object.
32700          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32701          */
32702         create : true
32703     });
32704
32705     Roo.tree.TreeLoader.superclass.constructor.call(this);
32706 };
32707
32708 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32709     /**
32710     * @cfg {String} dataUrl The URL from which to request a Json string which
32711     * specifies an array of node definition object representing the child nodes
32712     * to be loaded.
32713     */
32714     /**
32715     * @cfg {Object} baseParams (optional) An object containing properties which
32716     * specify HTTP parameters to be passed to each request for child nodes.
32717     */
32718     /**
32719     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32720     * created by this loader. If the attributes sent by the server have an attribute in this object,
32721     * they take priority.
32722     */
32723     /**
32724     * @cfg {Object} uiProviders (optional) An object containing properties which
32725     * 
32726     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32727     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32728     * <i>uiProvider</i> attribute of a returned child node is a string rather
32729     * than a reference to a TreeNodeUI implementation, this that string value
32730     * is used as a property name in the uiProviders object. You can define the provider named
32731     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32732     */
32733     uiProviders : {},
32734
32735     /**
32736     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32737     * child nodes before loading.
32738     */
32739     clearOnLoad : true,
32740
32741     /**
32742     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32743     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32744     * Grid query { data : [ .....] }
32745     */
32746     
32747     root : false,
32748      /**
32749     * @cfg {String} queryParam (optional) 
32750     * Name of the query as it will be passed on the querystring (defaults to 'node')
32751     * eg. the request will be ?node=[id]
32752     */
32753     
32754     
32755     queryParam: false,
32756     
32757     /**
32758      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32759      * This is called automatically when a node is expanded, but may be used to reload
32760      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32761      * @param {Roo.tree.TreeNode} node
32762      * @param {Function} callback
32763      */
32764     load : function(node, callback){
32765         if(this.clearOnLoad){
32766             while(node.firstChild){
32767                 node.removeChild(node.firstChild);
32768             }
32769         }
32770         if(node.attributes.children){ // preloaded json children
32771             var cs = node.attributes.children;
32772             for(var i = 0, len = cs.length; i < len; i++){
32773                 node.appendChild(this.createNode(cs[i]));
32774             }
32775             if(typeof callback == "function"){
32776                 callback();
32777             }
32778         }else if(this.dataUrl){
32779             this.requestData(node, callback);
32780         }
32781     },
32782
32783     getParams: function(node){
32784         var buf = [], bp = this.baseParams;
32785         for(var key in bp){
32786             if(typeof bp[key] != "function"){
32787                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32788             }
32789         }
32790         var n = this.queryParam === false ? 'node' : this.queryParam;
32791         buf.push(n + "=", encodeURIComponent(node.id));
32792         return buf.join("");
32793     },
32794
32795     requestData : function(node, callback){
32796         if(this.fireEvent("beforeload", this, node, callback) !== false){
32797             this.transId = Roo.Ajax.request({
32798                 method:this.requestMethod,
32799                 url: this.dataUrl||this.url,
32800                 success: this.handleResponse,
32801                 failure: this.handleFailure,
32802                 scope: this,
32803                 argument: {callback: callback, node: node},
32804                 params: this.getParams(node)
32805             });
32806         }else{
32807             // if the load is cancelled, make sure we notify
32808             // the node that we are done
32809             if(typeof callback == "function"){
32810                 callback();
32811             }
32812         }
32813     },
32814
32815     isLoading : function(){
32816         return this.transId ? true : false;
32817     },
32818
32819     abort : function(){
32820         if(this.isLoading()){
32821             Roo.Ajax.abort(this.transId);
32822         }
32823     },
32824
32825     // private
32826     createNode : function(attr){
32827         // apply baseAttrs, nice idea Corey!
32828         if(this.baseAttrs){
32829             Roo.applyIf(attr, this.baseAttrs);
32830         }
32831         if(this.applyLoader !== false){
32832             attr.loader = this;
32833         }
32834         // uiProvider = depreciated..
32835         
32836         if(typeof(attr.uiProvider) == 'string'){
32837            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32838                 /**  eval:var:attr */ eval(attr.uiProvider);
32839         }
32840         if(typeof(this.uiProviders['default']) != 'undefined') {
32841             attr.uiProvider = this.uiProviders['default'];
32842         }
32843         
32844         this.fireEvent('create', this, attr);
32845         
32846         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32847         return(attr.leaf ?
32848                         new Roo.tree.TreeNode(attr) :
32849                         new Roo.tree.AsyncTreeNode(attr));
32850     },
32851
32852     processResponse : function(response, node, callback){
32853         var json = response.responseText;
32854         try {
32855             
32856             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32857             if (this.root !== false) {
32858                 o = o[this.root];
32859             }
32860             
32861             for(var i = 0, len = o.length; i < len; i++){
32862                 var n = this.createNode(o[i]);
32863                 if(n){
32864                     node.appendChild(n);
32865                 }
32866             }
32867             if(typeof callback == "function"){
32868                 callback(this, node);
32869             }
32870         }catch(e){
32871             this.handleFailure(response);
32872         }
32873     },
32874
32875     handleResponse : function(response){
32876         this.transId = false;
32877         var a = response.argument;
32878         this.processResponse(response, a.node, a.callback);
32879         this.fireEvent("load", this, a.node, response);
32880     },
32881
32882     handleFailure : function(response){
32883         this.transId = false;
32884         var a = response.argument;
32885         this.fireEvent("loadexception", this, a.node, response);
32886         if(typeof a.callback == "function"){
32887             a.callback(this, a.node);
32888         }
32889     }
32890 });/*
32891  * Based on:
32892  * Ext JS Library 1.1.1
32893  * Copyright(c) 2006-2007, Ext JS, LLC.
32894  *
32895  * Originally Released Under LGPL - original licence link has changed is not relivant.
32896  *
32897  * Fork - LGPL
32898  * <script type="text/javascript">
32899  */
32900
32901 /**
32902 * @class Roo.tree.TreeFilter
32903 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32904 * @param {TreePanel} tree
32905 * @param {Object} config (optional)
32906  */
32907 Roo.tree.TreeFilter = function(tree, config){
32908     this.tree = tree;
32909     this.filtered = {};
32910     Roo.apply(this, config);
32911 };
32912
32913 Roo.tree.TreeFilter.prototype = {
32914     clearBlank:false,
32915     reverse:false,
32916     autoClear:false,
32917     remove:false,
32918
32919      /**
32920      * Filter the data by a specific attribute.
32921      * @param {String/RegExp} value Either string that the attribute value
32922      * should start with or a RegExp to test against the attribute
32923      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32924      * @param {TreeNode} startNode (optional) The node to start the filter at.
32925      */
32926     filter : function(value, attr, startNode){
32927         attr = attr || "text";
32928         var f;
32929         if(typeof value == "string"){
32930             var vlen = value.length;
32931             // auto clear empty filter
32932             if(vlen == 0 && this.clearBlank){
32933                 this.clear();
32934                 return;
32935             }
32936             value = value.toLowerCase();
32937             f = function(n){
32938                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32939             };
32940         }else if(value.exec){ // regex?
32941             f = function(n){
32942                 return value.test(n.attributes[attr]);
32943             };
32944         }else{
32945             throw 'Illegal filter type, must be string or regex';
32946         }
32947         this.filterBy(f, null, startNode);
32948         },
32949
32950     /**
32951      * Filter by a function. The passed function will be called with each
32952      * node in the tree (or from the startNode). If the function returns true, the node is kept
32953      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32954      * @param {Function} fn The filter function
32955      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32956      */
32957     filterBy : function(fn, scope, startNode){
32958         startNode = startNode || this.tree.root;
32959         if(this.autoClear){
32960             this.clear();
32961         }
32962         var af = this.filtered, rv = this.reverse;
32963         var f = function(n){
32964             if(n == startNode){
32965                 return true;
32966             }
32967             if(af[n.id]){
32968                 return false;
32969             }
32970             var m = fn.call(scope || n, n);
32971             if(!m || rv){
32972                 af[n.id] = n;
32973                 n.ui.hide();
32974                 return false;
32975             }
32976             return true;
32977         };
32978         startNode.cascade(f);
32979         if(this.remove){
32980            for(var id in af){
32981                if(typeof id != "function"){
32982                    var n = af[id];
32983                    if(n && n.parentNode){
32984                        n.parentNode.removeChild(n);
32985                    }
32986                }
32987            }
32988         }
32989     },
32990
32991     /**
32992      * Clears the current filter. Note: with the "remove" option
32993      * set a filter cannot be cleared.
32994      */
32995     clear : function(){
32996         var t = this.tree;
32997         var af = this.filtered;
32998         for(var id in af){
32999             if(typeof id != "function"){
33000                 var n = af[id];
33001                 if(n){
33002                     n.ui.show();
33003                 }
33004             }
33005         }
33006         this.filtered = {};
33007     }
33008 };
33009 /*
33010  * Based on:
33011  * Ext JS Library 1.1.1
33012  * Copyright(c) 2006-2007, Ext JS, LLC.
33013  *
33014  * Originally Released Under LGPL - original licence link has changed is not relivant.
33015  *
33016  * Fork - LGPL
33017  * <script type="text/javascript">
33018  */
33019  
33020
33021 /**
33022  * @class Roo.tree.TreeSorter
33023  * Provides sorting of nodes in a TreePanel
33024  * 
33025  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33026  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33027  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33028  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33029  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33030  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33031  * @constructor
33032  * @param {TreePanel} tree
33033  * @param {Object} config
33034  */
33035 Roo.tree.TreeSorter = function(tree, config){
33036     Roo.apply(this, config);
33037     tree.on("beforechildrenrendered", this.doSort, this);
33038     tree.on("append", this.updateSort, this);
33039     tree.on("insert", this.updateSort, this);
33040     
33041     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33042     var p = this.property || "text";
33043     var sortType = this.sortType;
33044     var fs = this.folderSort;
33045     var cs = this.caseSensitive === true;
33046     var leafAttr = this.leafAttr || 'leaf';
33047
33048     this.sortFn = function(n1, n2){
33049         if(fs){
33050             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33051                 return 1;
33052             }
33053             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33054                 return -1;
33055             }
33056         }
33057         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33058         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33059         if(v1 < v2){
33060                         return dsc ? +1 : -1;
33061                 }else if(v1 > v2){
33062                         return dsc ? -1 : +1;
33063         }else{
33064                 return 0;
33065         }
33066     };
33067 };
33068
33069 Roo.tree.TreeSorter.prototype = {
33070     doSort : function(node){
33071         node.sort(this.sortFn);
33072     },
33073     
33074     compareNodes : function(n1, n2){
33075         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33076     },
33077     
33078     updateSort : function(tree, node){
33079         if(node.childrenRendered){
33080             this.doSort.defer(1, this, [node]);
33081         }
33082     }
33083 };/*
33084  * Based on:
33085  * Ext JS Library 1.1.1
33086  * Copyright(c) 2006-2007, Ext JS, LLC.
33087  *
33088  * Originally Released Under LGPL - original licence link has changed is not relivant.
33089  *
33090  * Fork - LGPL
33091  * <script type="text/javascript">
33092  */
33093
33094 if(Roo.dd.DropZone){
33095     
33096 Roo.tree.TreeDropZone = function(tree, config){
33097     this.allowParentInsert = false;
33098     this.allowContainerDrop = false;
33099     this.appendOnly = false;
33100     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33101     this.tree = tree;
33102     this.lastInsertClass = "x-tree-no-status";
33103     this.dragOverData = {};
33104 };
33105
33106 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33107     ddGroup : "TreeDD",
33108     
33109     expandDelay : 1000,
33110     
33111     expandNode : function(node){
33112         if(node.hasChildNodes() && !node.isExpanded()){
33113             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33114         }
33115     },
33116     
33117     queueExpand : function(node){
33118         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33119     },
33120     
33121     cancelExpand : function(){
33122         if(this.expandProcId){
33123             clearTimeout(this.expandProcId);
33124             this.expandProcId = false;
33125         }
33126     },
33127     
33128     isValidDropPoint : function(n, pt, dd, e, data){
33129         if(!n || !data){ return false; }
33130         var targetNode = n.node;
33131         var dropNode = data.node;
33132         // default drop rules
33133         if(!(targetNode && targetNode.isTarget && pt)){
33134             return false;
33135         }
33136         if(pt == "append" && targetNode.allowChildren === false){
33137             return false;
33138         }
33139         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33140             return false;
33141         }
33142         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33143             return false;
33144         }
33145         // reuse the object
33146         var overEvent = this.dragOverData;
33147         overEvent.tree = this.tree;
33148         overEvent.target = targetNode;
33149         overEvent.data = data;
33150         overEvent.point = pt;
33151         overEvent.source = dd;
33152         overEvent.rawEvent = e;
33153         overEvent.dropNode = dropNode;
33154         overEvent.cancel = false;  
33155         var result = this.tree.fireEvent("nodedragover", overEvent);
33156         return overEvent.cancel === false && result !== false;
33157     },
33158     
33159     getDropPoint : function(e, n, dd){
33160         var tn = n.node;
33161         if(tn.isRoot){
33162             return tn.allowChildren !== false ? "append" : false; // always append for root
33163         }
33164         var dragEl = n.ddel;
33165         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33166         var y = Roo.lib.Event.getPageY(e);
33167         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33168         
33169         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33170         var noAppend = tn.allowChildren === false;
33171         if(this.appendOnly || tn.parentNode.allowChildren === false){
33172             return noAppend ? false : "append";
33173         }
33174         var noBelow = false;
33175         if(!this.allowParentInsert){
33176             noBelow = tn.hasChildNodes() && tn.isExpanded();
33177         }
33178         var q = (b - t) / (noAppend ? 2 : 3);
33179         if(y >= t && y < (t + q)){
33180             return "above";
33181         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33182             return "below";
33183         }else{
33184             return "append";
33185         }
33186     },
33187     
33188     onNodeEnter : function(n, dd, e, data){
33189         this.cancelExpand();
33190     },
33191     
33192     onNodeOver : function(n, dd, e, data){
33193         var pt = this.getDropPoint(e, n, dd);
33194         var node = n.node;
33195         
33196         // auto node expand check
33197         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33198             this.queueExpand(node);
33199         }else if(pt != "append"){
33200             this.cancelExpand();
33201         }
33202         
33203         // set the insert point style on the target node
33204         var returnCls = this.dropNotAllowed;
33205         if(this.isValidDropPoint(n, pt, dd, e, data)){
33206            if(pt){
33207                var el = n.ddel;
33208                var cls;
33209                if(pt == "above"){
33210                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33211                    cls = "x-tree-drag-insert-above";
33212                }else if(pt == "below"){
33213                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33214                    cls = "x-tree-drag-insert-below";
33215                }else{
33216                    returnCls = "x-tree-drop-ok-append";
33217                    cls = "x-tree-drag-append";
33218                }
33219                if(this.lastInsertClass != cls){
33220                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33221                    this.lastInsertClass = cls;
33222                }
33223            }
33224        }
33225        return returnCls;
33226     },
33227     
33228     onNodeOut : function(n, dd, e, data){
33229         this.cancelExpand();
33230         this.removeDropIndicators(n);
33231     },
33232     
33233     onNodeDrop : function(n, dd, e, data){
33234         var point = this.getDropPoint(e, n, dd);
33235         var targetNode = n.node;
33236         targetNode.ui.startDrop();
33237         if(!this.isValidDropPoint(n, point, dd, e, data)){
33238             targetNode.ui.endDrop();
33239             return false;
33240         }
33241         // first try to find the drop node
33242         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33243         var dropEvent = {
33244             tree : this.tree,
33245             target: targetNode,
33246             data: data,
33247             point: point,
33248             source: dd,
33249             rawEvent: e,
33250             dropNode: dropNode,
33251             cancel: !dropNode   
33252         };
33253         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33254         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33255             targetNode.ui.endDrop();
33256             return false;
33257         }
33258         // allow target changing
33259         targetNode = dropEvent.target;
33260         if(point == "append" && !targetNode.isExpanded()){
33261             targetNode.expand(false, null, function(){
33262                 this.completeDrop(dropEvent);
33263             }.createDelegate(this));
33264         }else{
33265             this.completeDrop(dropEvent);
33266         }
33267         return true;
33268     },
33269     
33270     completeDrop : function(de){
33271         var ns = de.dropNode, p = de.point, t = de.target;
33272         if(!(ns instanceof Array)){
33273             ns = [ns];
33274         }
33275         var n;
33276         for(var i = 0, len = ns.length; i < len; i++){
33277             n = ns[i];
33278             if(p == "above"){
33279                 t.parentNode.insertBefore(n, t);
33280             }else if(p == "below"){
33281                 t.parentNode.insertBefore(n, t.nextSibling);
33282             }else{
33283                 t.appendChild(n);
33284             }
33285         }
33286         n.ui.focus();
33287         if(this.tree.hlDrop){
33288             n.ui.highlight();
33289         }
33290         t.ui.endDrop();
33291         this.tree.fireEvent("nodedrop", de);
33292     },
33293     
33294     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33295         if(this.tree.hlDrop){
33296             dropNode.ui.focus();
33297             dropNode.ui.highlight();
33298         }
33299         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33300     },
33301     
33302     getTree : function(){
33303         return this.tree;
33304     },
33305     
33306     removeDropIndicators : function(n){
33307         if(n && n.ddel){
33308             var el = n.ddel;
33309             Roo.fly(el).removeClass([
33310                     "x-tree-drag-insert-above",
33311                     "x-tree-drag-insert-below",
33312                     "x-tree-drag-append"]);
33313             this.lastInsertClass = "_noclass";
33314         }
33315     },
33316     
33317     beforeDragDrop : function(target, e, id){
33318         this.cancelExpand();
33319         return true;
33320     },
33321     
33322     afterRepair : function(data){
33323         if(data && Roo.enableFx){
33324             data.node.ui.highlight();
33325         }
33326         this.hideProxy();
33327     }    
33328 });
33329
33330 }
33331 /*
33332  * Based on:
33333  * Ext JS Library 1.1.1
33334  * Copyright(c) 2006-2007, Ext JS, LLC.
33335  *
33336  * Originally Released Under LGPL - original licence link has changed is not relivant.
33337  *
33338  * Fork - LGPL
33339  * <script type="text/javascript">
33340  */
33341  
33342
33343 if(Roo.dd.DragZone){
33344 Roo.tree.TreeDragZone = function(tree, config){
33345     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33346     this.tree = tree;
33347 };
33348
33349 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33350     ddGroup : "TreeDD",
33351     
33352     onBeforeDrag : function(data, e){
33353         var n = data.node;
33354         return n && n.draggable && !n.disabled;
33355     },
33356     
33357     onInitDrag : function(e){
33358         var data = this.dragData;
33359         this.tree.getSelectionModel().select(data.node);
33360         this.proxy.update("");
33361         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33362         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33363     },
33364     
33365     getRepairXY : function(e, data){
33366         return data.node.ui.getDDRepairXY();
33367     },
33368     
33369     onEndDrag : function(data, e){
33370         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33371     },
33372     
33373     onValidDrop : function(dd, e, id){
33374         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33375         this.hideProxy();
33376     },
33377     
33378     beforeInvalidDrop : function(e, id){
33379         // this scrolls the original position back into view
33380         var sm = this.tree.getSelectionModel();
33381         sm.clearSelections();
33382         sm.select(this.dragData.node);
33383     }
33384 });
33385 }/*
33386  * Based on:
33387  * Ext JS Library 1.1.1
33388  * Copyright(c) 2006-2007, Ext JS, LLC.
33389  *
33390  * Originally Released Under LGPL - original licence link has changed is not relivant.
33391  *
33392  * Fork - LGPL
33393  * <script type="text/javascript">
33394  */
33395 /**
33396  * @class Roo.tree.TreeEditor
33397  * @extends Roo.Editor
33398  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33399  * as the editor field.
33400  * @constructor
33401  * @param {TreePanel} tree
33402  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33403  */
33404 Roo.tree.TreeEditor = function(tree, config){
33405     config = config || {};
33406     var field = config.events ? config : new Roo.form.TextField(config);
33407     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33408
33409     this.tree = tree;
33410
33411     tree.on('beforeclick', this.beforeNodeClick, this);
33412     tree.getTreeEl().on('mousedown', this.hide, this);
33413     this.on('complete', this.updateNode, this);
33414     this.on('beforestartedit', this.fitToTree, this);
33415     this.on('startedit', this.bindScroll, this, {delay:10});
33416     this.on('specialkey', this.onSpecialKey, this);
33417 };
33418
33419 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33420     /**
33421      * @cfg {String} alignment
33422      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33423      */
33424     alignment: "l-l",
33425     // inherit
33426     autoSize: false,
33427     /**
33428      * @cfg {Boolean} hideEl
33429      * True to hide the bound element while the editor is displayed (defaults to false)
33430      */
33431     hideEl : false,
33432     /**
33433      * @cfg {String} cls
33434      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33435      */
33436     cls: "x-small-editor x-tree-editor",
33437     /**
33438      * @cfg {Boolean} shim
33439      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33440      */
33441     shim:false,
33442     // inherit
33443     shadow:"frame",
33444     /**
33445      * @cfg {Number} maxWidth
33446      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33447      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33448      * scroll and client offsets into account prior to each edit.
33449      */
33450     maxWidth: 250,
33451
33452     editDelay : 350,
33453
33454     // private
33455     fitToTree : function(ed, el){
33456         var td = this.tree.getTreeEl().dom, nd = el.dom;
33457         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33458             td.scrollLeft = nd.offsetLeft;
33459         }
33460         var w = Math.min(
33461                 this.maxWidth,
33462                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33463         this.setSize(w, '');
33464     },
33465
33466     // private
33467     triggerEdit : function(node){
33468         this.completeEdit();
33469         this.editNode = node;
33470         this.startEdit(node.ui.textNode, node.text);
33471     },
33472
33473     // private
33474     bindScroll : function(){
33475         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33476     },
33477
33478     // private
33479     beforeNodeClick : function(node, e){
33480         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33481         this.lastClick = new Date();
33482         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33483             e.stopEvent();
33484             this.triggerEdit(node);
33485             return false;
33486         }
33487     },
33488
33489     // private
33490     updateNode : function(ed, value){
33491         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33492         this.editNode.setText(value);
33493     },
33494
33495     // private
33496     onHide : function(){
33497         Roo.tree.TreeEditor.superclass.onHide.call(this);
33498         if(this.editNode){
33499             this.editNode.ui.focus();
33500         }
33501     },
33502
33503     // private
33504     onSpecialKey : function(field, e){
33505         var k = e.getKey();
33506         if(k == e.ESC){
33507             e.stopEvent();
33508             this.cancelEdit();
33509         }else if(k == e.ENTER && !e.hasModifier()){
33510             e.stopEvent();
33511             this.completeEdit();
33512         }
33513     }
33514 });//<Script type="text/javascript">
33515 /*
33516  * Based on:
33517  * Ext JS Library 1.1.1
33518  * Copyright(c) 2006-2007, Ext JS, LLC.
33519  *
33520  * Originally Released Under LGPL - original licence link has changed is not relivant.
33521  *
33522  * Fork - LGPL
33523  * <script type="text/javascript">
33524  */
33525  
33526 /**
33527  * Not documented??? - probably should be...
33528  */
33529
33530 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33531     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33532     
33533     renderElements : function(n, a, targetNode, bulkRender){
33534         //consel.log("renderElements?");
33535         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33536
33537         var t = n.getOwnerTree();
33538         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33539         
33540         var cols = t.columns;
33541         var bw = t.borderWidth;
33542         var c = cols[0];
33543         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33544          var cb = typeof a.checked == "boolean";
33545         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33546         var colcls = 'x-t-' + tid + '-c0';
33547         var buf = [
33548             '<li class="x-tree-node">',
33549             
33550                 
33551                 '<div class="x-tree-node-el ', a.cls,'">',
33552                     // extran...
33553                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33554                 
33555                 
33556                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33557                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33558                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33559                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33560                            (a.iconCls ? ' '+a.iconCls : ''),
33561                            '" unselectable="on" />',
33562                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33563                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33564                              
33565                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33566                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33567                             '<span unselectable="on" qtip="' + tx + '">',
33568                              tx,
33569                              '</span></a>' ,
33570                     '</div>',
33571                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33572                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33573                  ];
33574         for(var i = 1, len = cols.length; i < len; i++){
33575             c = cols[i];
33576             colcls = 'x-t-' + tid + '-c' +i;
33577             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33578             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33579                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33580                       "</div>");
33581          }
33582          
33583          buf.push(
33584             '</a>',
33585             '<div class="x-clear"></div></div>',
33586             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33587             "</li>");
33588         
33589         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33590             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33591                                 n.nextSibling.ui.getEl(), buf.join(""));
33592         }else{
33593             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33594         }
33595         var el = this.wrap.firstChild;
33596         this.elRow = el;
33597         this.elNode = el.firstChild;
33598         this.ranchor = el.childNodes[1];
33599         this.ctNode = this.wrap.childNodes[1];
33600         var cs = el.firstChild.childNodes;
33601         this.indentNode = cs[0];
33602         this.ecNode = cs[1];
33603         this.iconNode = cs[2];
33604         var index = 3;
33605         if(cb){
33606             this.checkbox = cs[3];
33607             index++;
33608         }
33609         this.anchor = cs[index];
33610         
33611         this.textNode = cs[index].firstChild;
33612         
33613         //el.on("click", this.onClick, this);
33614         //el.on("dblclick", this.onDblClick, this);
33615         
33616         
33617        // console.log(this);
33618     },
33619     initEvents : function(){
33620         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33621         
33622             
33623         var a = this.ranchor;
33624
33625         var el = Roo.get(a);
33626
33627         if(Roo.isOpera){ // opera render bug ignores the CSS
33628             el.setStyle("text-decoration", "none");
33629         }
33630
33631         el.on("click", this.onClick, this);
33632         el.on("dblclick", this.onDblClick, this);
33633         el.on("contextmenu", this.onContextMenu, this);
33634         
33635     },
33636     
33637     /*onSelectedChange : function(state){
33638         if(state){
33639             this.focus();
33640             this.addClass("x-tree-selected");
33641         }else{
33642             //this.blur();
33643             this.removeClass("x-tree-selected");
33644         }
33645     },*/
33646     addClass : function(cls){
33647         if(this.elRow){
33648             Roo.fly(this.elRow).addClass(cls);
33649         }
33650         
33651     },
33652     
33653     
33654     removeClass : function(cls){
33655         if(this.elRow){
33656             Roo.fly(this.elRow).removeClass(cls);
33657         }
33658     }
33659
33660     
33661     
33662 });//<Script type="text/javascript">
33663
33664 /*
33665  * Based on:
33666  * Ext JS Library 1.1.1
33667  * Copyright(c) 2006-2007, Ext JS, LLC.
33668  *
33669  * Originally Released Under LGPL - original licence link has changed is not relivant.
33670  *
33671  * Fork - LGPL
33672  * <script type="text/javascript">
33673  */
33674  
33675
33676 /**
33677  * @class Roo.tree.ColumnTree
33678  * @extends Roo.data.TreePanel
33679  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33680  * @cfg {int} borderWidth  compined right/left border allowance
33681  * @constructor
33682  * @param {String/HTMLElement/Element} el The container element
33683  * @param {Object} config
33684  */
33685 Roo.tree.ColumnTree =  function(el, config)
33686 {
33687    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33688    this.addEvents({
33689         /**
33690         * @event resize
33691         * Fire this event on a container when it resizes
33692         * @param {int} w Width
33693         * @param {int} h Height
33694         */
33695        "resize" : true
33696     });
33697     this.on('resize', this.onResize, this);
33698 };
33699
33700 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33701     //lines:false,
33702     
33703     
33704     borderWidth: Roo.isBorderBox ? 0 : 2, 
33705     headEls : false,
33706     
33707     render : function(){
33708         // add the header.....
33709        
33710         Roo.tree.ColumnTree.superclass.render.apply(this);
33711         
33712         this.el.addClass('x-column-tree');
33713         
33714         this.headers = this.el.createChild(
33715             {cls:'x-tree-headers'},this.innerCt.dom);
33716    
33717         var cols = this.columns, c;
33718         var totalWidth = 0;
33719         this.headEls = [];
33720         var  len = cols.length;
33721         for(var i = 0; i < len; i++){
33722              c = cols[i];
33723              totalWidth += c.width;
33724             this.headEls.push(this.headers.createChild({
33725                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33726                  cn: {
33727                      cls:'x-tree-hd-text',
33728                      html: c.header
33729                  },
33730                  style:'width:'+(c.width-this.borderWidth)+'px;'
33731              }));
33732         }
33733         this.headers.createChild({cls:'x-clear'});
33734         // prevent floats from wrapping when clipped
33735         this.headers.setWidth(totalWidth);
33736         //this.innerCt.setWidth(totalWidth);
33737         this.innerCt.setStyle({ overflow: 'auto' });
33738         this.onResize(this.width, this.height);
33739              
33740         
33741     },
33742     onResize : function(w,h)
33743     {
33744         this.height = h;
33745         this.width = w;
33746         // resize cols..
33747         this.innerCt.setWidth(this.width);
33748         this.innerCt.setHeight(this.height-20);
33749         
33750         // headers...
33751         var cols = this.columns, c;
33752         var totalWidth = 0;
33753         var expEl = false;
33754         var len = cols.length;
33755         for(var i = 0; i < len; i++){
33756             c = cols[i];
33757             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33758                 // it's the expander..
33759                 expEl  = this.headEls[i];
33760                 continue;
33761             }
33762             totalWidth += c.width;
33763             
33764         }
33765         if (expEl) {
33766             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33767         }
33768         this.headers.setWidth(w-20);
33769
33770         
33771         
33772         
33773     }
33774 });
33775 /*
33776  * Based on:
33777  * Ext JS Library 1.1.1
33778  * Copyright(c) 2006-2007, Ext JS, LLC.
33779  *
33780  * Originally Released Under LGPL - original licence link has changed is not relivant.
33781  *
33782  * Fork - LGPL
33783  * <script type="text/javascript">
33784  */
33785  
33786 /**
33787  * @class Roo.menu.Menu
33788  * @extends Roo.util.Observable
33789  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33790  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33791  * @constructor
33792  * Creates a new Menu
33793  * @param {Object} config Configuration options
33794  */
33795 Roo.menu.Menu = function(config){
33796     Roo.apply(this, config);
33797     this.id = this.id || Roo.id();
33798     this.addEvents({
33799         /**
33800          * @event beforeshow
33801          * Fires before this menu is displayed
33802          * @param {Roo.menu.Menu} this
33803          */
33804         beforeshow : true,
33805         /**
33806          * @event beforehide
33807          * Fires before this menu is hidden
33808          * @param {Roo.menu.Menu} this
33809          */
33810         beforehide : true,
33811         /**
33812          * @event show
33813          * Fires after this menu is displayed
33814          * @param {Roo.menu.Menu} this
33815          */
33816         show : true,
33817         /**
33818          * @event hide
33819          * Fires after this menu is hidden
33820          * @param {Roo.menu.Menu} this
33821          */
33822         hide : true,
33823         /**
33824          * @event click
33825          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33826          * @param {Roo.menu.Menu} this
33827          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33828          * @param {Roo.EventObject} e
33829          */
33830         click : true,
33831         /**
33832          * @event mouseover
33833          * Fires when the mouse is hovering over this menu
33834          * @param {Roo.menu.Menu} this
33835          * @param {Roo.EventObject} e
33836          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33837          */
33838         mouseover : true,
33839         /**
33840          * @event mouseout
33841          * Fires when the mouse exits this menu
33842          * @param {Roo.menu.Menu} this
33843          * @param {Roo.EventObject} e
33844          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33845          */
33846         mouseout : true,
33847         /**
33848          * @event itemclick
33849          * Fires when a menu item contained in this menu is clicked
33850          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33851          * @param {Roo.EventObject} e
33852          */
33853         itemclick: true
33854     });
33855     if (this.registerMenu) {
33856         Roo.menu.MenuMgr.register(this);
33857     }
33858     
33859     var mis = this.items;
33860     this.items = new Roo.util.MixedCollection();
33861     if(mis){
33862         this.add.apply(this, mis);
33863     }
33864 };
33865
33866 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33867     /**
33868      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33869      */
33870     minWidth : 120,
33871     /**
33872      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33873      * for bottom-right shadow (defaults to "sides")
33874      */
33875     shadow : "sides",
33876     /**
33877      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33878      * this menu (defaults to "tl-tr?")
33879      */
33880     subMenuAlign : "tl-tr?",
33881     /**
33882      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33883      * relative to its element of origin (defaults to "tl-bl?")
33884      */
33885     defaultAlign : "tl-bl?",
33886     /**
33887      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33888      */
33889     allowOtherMenus : false,
33890     /**
33891      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33892      */
33893     registerMenu : true,
33894
33895     hidden:true,
33896
33897     // private
33898     render : function(){
33899         if(this.el){
33900             return;
33901         }
33902         var el = this.el = new Roo.Layer({
33903             cls: "x-menu",
33904             shadow:this.shadow,
33905             constrain: false,
33906             parentEl: this.parentEl || document.body,
33907             zindex:15000
33908         });
33909
33910         this.keyNav = new Roo.menu.MenuNav(this);
33911
33912         if(this.plain){
33913             el.addClass("x-menu-plain");
33914         }
33915         if(this.cls){
33916             el.addClass(this.cls);
33917         }
33918         // generic focus element
33919         this.focusEl = el.createChild({
33920             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33921         });
33922         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33923         ul.on("click", this.onClick, this);
33924         ul.on("mouseover", this.onMouseOver, this);
33925         ul.on("mouseout", this.onMouseOut, this);
33926         this.items.each(function(item){
33927             var li = document.createElement("li");
33928             li.className = "x-menu-list-item";
33929             ul.dom.appendChild(li);
33930             item.render(li, this);
33931         }, this);
33932         this.ul = ul;
33933         this.autoWidth();
33934     },
33935
33936     // private
33937     autoWidth : function(){
33938         var el = this.el, ul = this.ul;
33939         if(!el){
33940             return;
33941         }
33942         var w = this.width;
33943         if(w){
33944             el.setWidth(w);
33945         }else if(Roo.isIE){
33946             el.setWidth(this.minWidth);
33947             var t = el.dom.offsetWidth; // force recalc
33948             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33949         }
33950     },
33951
33952     // private
33953     delayAutoWidth : function(){
33954         if(this.rendered){
33955             if(!this.awTask){
33956                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33957             }
33958             this.awTask.delay(20);
33959         }
33960     },
33961
33962     // private
33963     findTargetItem : function(e){
33964         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33965         if(t && t.menuItemId){
33966             return this.items.get(t.menuItemId);
33967         }
33968     },
33969
33970     // private
33971     onClick : function(e){
33972         var t;
33973         if(t = this.findTargetItem(e)){
33974             t.onClick(e);
33975             this.fireEvent("click", this, t, e);
33976         }
33977     },
33978
33979     // private
33980     setActiveItem : function(item, autoExpand){
33981         if(item != this.activeItem){
33982             if(this.activeItem){
33983                 this.activeItem.deactivate();
33984             }
33985             this.activeItem = item;
33986             item.activate(autoExpand);
33987         }else if(autoExpand){
33988             item.expandMenu();
33989         }
33990     },
33991
33992     // private
33993     tryActivate : function(start, step){
33994         var items = this.items;
33995         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33996             var item = items.get(i);
33997             if(!item.disabled && item.canActivate){
33998                 this.setActiveItem(item, false);
33999                 return item;
34000             }
34001         }
34002         return false;
34003     },
34004
34005     // private
34006     onMouseOver : function(e){
34007         var t;
34008         if(t = this.findTargetItem(e)){
34009             if(t.canActivate && !t.disabled){
34010                 this.setActiveItem(t, true);
34011             }
34012         }
34013         this.fireEvent("mouseover", this, e, t);
34014     },
34015
34016     // private
34017     onMouseOut : function(e){
34018         var t;
34019         if(t = this.findTargetItem(e)){
34020             if(t == this.activeItem && t.shouldDeactivate(e)){
34021                 this.activeItem.deactivate();
34022                 delete this.activeItem;
34023             }
34024         }
34025         this.fireEvent("mouseout", this, e, t);
34026     },
34027
34028     /**
34029      * Read-only.  Returns true if the menu is currently displayed, else false.
34030      * @type Boolean
34031      */
34032     isVisible : function(){
34033         return this.el && !this.hidden;
34034     },
34035
34036     /**
34037      * Displays this menu relative to another element
34038      * @param {String/HTMLElement/Roo.Element} element The element to align to
34039      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34040      * the element (defaults to this.defaultAlign)
34041      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34042      */
34043     show : function(el, pos, parentMenu){
34044         this.parentMenu = parentMenu;
34045         if(!this.el){
34046             this.render();
34047         }
34048         this.fireEvent("beforeshow", this);
34049         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34050     },
34051
34052     /**
34053      * Displays this menu at a specific xy position
34054      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34055      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34056      */
34057     showAt : function(xy, parentMenu, /* private: */_e){
34058         this.parentMenu = parentMenu;
34059         if(!this.el){
34060             this.render();
34061         }
34062         if(_e !== false){
34063             this.fireEvent("beforeshow", this);
34064             xy = this.el.adjustForConstraints(xy);
34065         }
34066         this.el.setXY(xy);
34067         this.el.show();
34068         this.hidden = false;
34069         this.focus();
34070         this.fireEvent("show", this);
34071     },
34072
34073     focus : function(){
34074         if(!this.hidden){
34075             this.doFocus.defer(50, this);
34076         }
34077     },
34078
34079     doFocus : function(){
34080         if(!this.hidden){
34081             this.focusEl.focus();
34082         }
34083     },
34084
34085     /**
34086      * Hides this menu and optionally all parent menus
34087      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34088      */
34089     hide : function(deep){
34090         if(this.el && this.isVisible()){
34091             this.fireEvent("beforehide", this);
34092             if(this.activeItem){
34093                 this.activeItem.deactivate();
34094                 this.activeItem = null;
34095             }
34096             this.el.hide();
34097             this.hidden = true;
34098             this.fireEvent("hide", this);
34099         }
34100         if(deep === true && this.parentMenu){
34101             this.parentMenu.hide(true);
34102         }
34103     },
34104
34105     /**
34106      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34107      * Any of the following are valid:
34108      * <ul>
34109      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34110      * <li>An HTMLElement object which will be converted to a menu item</li>
34111      * <li>A menu item config object that will be created as a new menu item</li>
34112      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34113      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34114      * </ul>
34115      * Usage:
34116      * <pre><code>
34117 // Create the menu
34118 var menu = new Roo.menu.Menu();
34119
34120 // Create a menu item to add by reference
34121 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34122
34123 // Add a bunch of items at once using different methods.
34124 // Only the last item added will be returned.
34125 var item = menu.add(
34126     menuItem,                // add existing item by ref
34127     'Dynamic Item',          // new TextItem
34128     '-',                     // new separator
34129     { text: 'Config Item' }  // new item by config
34130 );
34131 </code></pre>
34132      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34133      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34134      */
34135     add : function(){
34136         var a = arguments, l = a.length, item;
34137         for(var i = 0; i < l; i++){
34138             var el = a[i];
34139             if ((typeof(el) == "object") && el.xtype && el.xns) {
34140                 el = Roo.factory(el, Roo.menu);
34141             }
34142             
34143             if(el.render){ // some kind of Item
34144                 item = this.addItem(el);
34145             }else if(typeof el == "string"){ // string
34146                 if(el == "separator" || el == "-"){
34147                     item = this.addSeparator();
34148                 }else{
34149                     item = this.addText(el);
34150                 }
34151             }else if(el.tagName || el.el){ // element
34152                 item = this.addElement(el);
34153             }else if(typeof el == "object"){ // must be menu item config?
34154                 item = this.addMenuItem(el);
34155             }
34156         }
34157         return item;
34158     },
34159
34160     /**
34161      * Returns this menu's underlying {@link Roo.Element} object
34162      * @return {Roo.Element} The element
34163      */
34164     getEl : function(){
34165         if(!this.el){
34166             this.render();
34167         }
34168         return this.el;
34169     },
34170
34171     /**
34172      * Adds a separator bar to the menu
34173      * @return {Roo.menu.Item} The menu item that was added
34174      */
34175     addSeparator : function(){
34176         return this.addItem(new Roo.menu.Separator());
34177     },
34178
34179     /**
34180      * Adds an {@link Roo.Element} object to the menu
34181      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34182      * @return {Roo.menu.Item} The menu item that was added
34183      */
34184     addElement : function(el){
34185         return this.addItem(new Roo.menu.BaseItem(el));
34186     },
34187
34188     /**
34189      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34190      * @param {Roo.menu.Item} item The menu item to add
34191      * @return {Roo.menu.Item} The menu item that was added
34192      */
34193     addItem : function(item){
34194         this.items.add(item);
34195         if(this.ul){
34196             var li = document.createElement("li");
34197             li.className = "x-menu-list-item";
34198             this.ul.dom.appendChild(li);
34199             item.render(li, this);
34200             this.delayAutoWidth();
34201         }
34202         return item;
34203     },
34204
34205     /**
34206      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34207      * @param {Object} config A MenuItem config object
34208      * @return {Roo.menu.Item} The menu item that was added
34209      */
34210     addMenuItem : function(config){
34211         if(!(config instanceof Roo.menu.Item)){
34212             if(typeof config.checked == "boolean"){ // must be check menu item config?
34213                 config = new Roo.menu.CheckItem(config);
34214             }else{
34215                 config = new Roo.menu.Item(config);
34216             }
34217         }
34218         return this.addItem(config);
34219     },
34220
34221     /**
34222      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34223      * @param {String} text The text to display in the menu item
34224      * @return {Roo.menu.Item} The menu item that was added
34225      */
34226     addText : function(text){
34227         return this.addItem(new Roo.menu.TextItem({ text : text }));
34228     },
34229
34230     /**
34231      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34232      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34233      * @param {Roo.menu.Item} item The menu item to add
34234      * @return {Roo.menu.Item} The menu item that was added
34235      */
34236     insert : function(index, item){
34237         this.items.insert(index, item);
34238         if(this.ul){
34239             var li = document.createElement("li");
34240             li.className = "x-menu-list-item";
34241             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34242             item.render(li, this);
34243             this.delayAutoWidth();
34244         }
34245         return item;
34246     },
34247
34248     /**
34249      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34250      * @param {Roo.menu.Item} item The menu item to remove
34251      */
34252     remove : function(item){
34253         this.items.removeKey(item.id);
34254         item.destroy();
34255     },
34256
34257     /**
34258      * Removes and destroys all items in the menu
34259      */
34260     removeAll : function(){
34261         var f;
34262         while(f = this.items.first()){
34263             this.remove(f);
34264         }
34265     }
34266 });
34267
34268 // MenuNav is a private utility class used internally by the Menu
34269 Roo.menu.MenuNav = function(menu){
34270     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34271     this.scope = this.menu = menu;
34272 };
34273
34274 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34275     doRelay : function(e, h){
34276         var k = e.getKey();
34277         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34278             this.menu.tryActivate(0, 1);
34279             return false;
34280         }
34281         return h.call(this.scope || this, e, this.menu);
34282     },
34283
34284     up : function(e, m){
34285         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34286             m.tryActivate(m.items.length-1, -1);
34287         }
34288     },
34289
34290     down : function(e, m){
34291         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34292             m.tryActivate(0, 1);
34293         }
34294     },
34295
34296     right : function(e, m){
34297         if(m.activeItem){
34298             m.activeItem.expandMenu(true);
34299         }
34300     },
34301
34302     left : function(e, m){
34303         m.hide();
34304         if(m.parentMenu && m.parentMenu.activeItem){
34305             m.parentMenu.activeItem.activate();
34306         }
34307     },
34308
34309     enter : function(e, m){
34310         if(m.activeItem){
34311             e.stopPropagation();
34312             m.activeItem.onClick(e);
34313             m.fireEvent("click", this, m.activeItem);
34314             return true;
34315         }
34316     }
34317 });/*
34318  * Based on:
34319  * Ext JS Library 1.1.1
34320  * Copyright(c) 2006-2007, Ext JS, LLC.
34321  *
34322  * Originally Released Under LGPL - original licence link has changed is not relivant.
34323  *
34324  * Fork - LGPL
34325  * <script type="text/javascript">
34326  */
34327  
34328 /**
34329  * @class Roo.menu.MenuMgr
34330  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34331  * @singleton
34332  */
34333 Roo.menu.MenuMgr = function(){
34334    var menus, active, groups = {}, attached = false, lastShow = new Date();
34335
34336    // private - called when first menu is created
34337    function init(){
34338        menus = {};
34339        active = new Roo.util.MixedCollection();
34340        Roo.get(document).addKeyListener(27, function(){
34341            if(active.length > 0){
34342                hideAll();
34343            }
34344        });
34345    }
34346
34347    // private
34348    function hideAll(){
34349        if(active && active.length > 0){
34350            var c = active.clone();
34351            c.each(function(m){
34352                m.hide();
34353            });
34354        }
34355    }
34356
34357    // private
34358    function onHide(m){
34359        active.remove(m);
34360        if(active.length < 1){
34361            Roo.get(document).un("mousedown", onMouseDown);
34362            attached = false;
34363        }
34364    }
34365
34366    // private
34367    function onShow(m){
34368        var last = active.last();
34369        lastShow = new Date();
34370        active.add(m);
34371        if(!attached){
34372            Roo.get(document).on("mousedown", onMouseDown);
34373            attached = true;
34374        }
34375        if(m.parentMenu){
34376           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34377           m.parentMenu.activeChild = m;
34378        }else if(last && last.isVisible()){
34379           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34380        }
34381    }
34382
34383    // private
34384    function onBeforeHide(m){
34385        if(m.activeChild){
34386            m.activeChild.hide();
34387        }
34388        if(m.autoHideTimer){
34389            clearTimeout(m.autoHideTimer);
34390            delete m.autoHideTimer;
34391        }
34392    }
34393
34394    // private
34395    function onBeforeShow(m){
34396        var pm = m.parentMenu;
34397        if(!pm && !m.allowOtherMenus){
34398            hideAll();
34399        }else if(pm && pm.activeChild && active != m){
34400            pm.activeChild.hide();
34401        }
34402    }
34403
34404    // private
34405    function onMouseDown(e){
34406        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34407            hideAll();
34408        }
34409    }
34410
34411    // private
34412    function onBeforeCheck(mi, state){
34413        if(state){
34414            var g = groups[mi.group];
34415            for(var i = 0, l = g.length; i < l; i++){
34416                if(g[i] != mi){
34417                    g[i].setChecked(false);
34418                }
34419            }
34420        }
34421    }
34422
34423    return {
34424
34425        /**
34426         * Hides all menus that are currently visible
34427         */
34428        hideAll : function(){
34429             hideAll();  
34430        },
34431
34432        // private
34433        register : function(menu){
34434            if(!menus){
34435                init();
34436            }
34437            menus[menu.id] = menu;
34438            menu.on("beforehide", onBeforeHide);
34439            menu.on("hide", onHide);
34440            menu.on("beforeshow", onBeforeShow);
34441            menu.on("show", onShow);
34442            var g = menu.group;
34443            if(g && menu.events["checkchange"]){
34444                if(!groups[g]){
34445                    groups[g] = [];
34446                }
34447                groups[g].push(menu);
34448                menu.on("checkchange", onCheck);
34449            }
34450        },
34451
34452         /**
34453          * Returns a {@link Roo.menu.Menu} object
34454          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34455          * be used to generate and return a new Menu instance.
34456          */
34457        get : function(menu){
34458            if(typeof menu == "string"){ // menu id
34459                return menus[menu];
34460            }else if(menu.events){  // menu instance
34461                return menu;
34462            }else if(typeof menu.length == 'number'){ // array of menu items?
34463                return new Roo.menu.Menu({items:menu});
34464            }else{ // otherwise, must be a config
34465                return new Roo.menu.Menu(menu);
34466            }
34467        },
34468
34469        // private
34470        unregister : function(menu){
34471            delete menus[menu.id];
34472            menu.un("beforehide", onBeforeHide);
34473            menu.un("hide", onHide);
34474            menu.un("beforeshow", onBeforeShow);
34475            menu.un("show", onShow);
34476            var g = menu.group;
34477            if(g && menu.events["checkchange"]){
34478                groups[g].remove(menu);
34479                menu.un("checkchange", onCheck);
34480            }
34481        },
34482
34483        // private
34484        registerCheckable : function(menuItem){
34485            var g = menuItem.group;
34486            if(g){
34487                if(!groups[g]){
34488                    groups[g] = [];
34489                }
34490                groups[g].push(menuItem);
34491                menuItem.on("beforecheckchange", onBeforeCheck);
34492            }
34493        },
34494
34495        // private
34496        unregisterCheckable : function(menuItem){
34497            var g = menuItem.group;
34498            if(g){
34499                groups[g].remove(menuItem);
34500                menuItem.un("beforecheckchange", onBeforeCheck);
34501            }
34502        }
34503    };
34504 }();/*
34505  * Based on:
34506  * Ext JS Library 1.1.1
34507  * Copyright(c) 2006-2007, Ext JS, LLC.
34508  *
34509  * Originally Released Under LGPL - original licence link has changed is not relivant.
34510  *
34511  * Fork - LGPL
34512  * <script type="text/javascript">
34513  */
34514  
34515
34516 /**
34517  * @class Roo.menu.BaseItem
34518  * @extends Roo.Component
34519  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34520  * management and base configuration options shared by all menu components.
34521  * @constructor
34522  * Creates a new BaseItem
34523  * @param {Object} config Configuration options
34524  */
34525 Roo.menu.BaseItem = function(config){
34526     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34527
34528     this.addEvents({
34529         /**
34530          * @event click
34531          * Fires when this item is clicked
34532          * @param {Roo.menu.BaseItem} this
34533          * @param {Roo.EventObject} e
34534          */
34535         click: true,
34536         /**
34537          * @event activate
34538          * Fires when this item is activated
34539          * @param {Roo.menu.BaseItem} this
34540          */
34541         activate : true,
34542         /**
34543          * @event deactivate
34544          * Fires when this item is deactivated
34545          * @param {Roo.menu.BaseItem} this
34546          */
34547         deactivate : true
34548     });
34549
34550     if(this.handler){
34551         this.on("click", this.handler, this.scope, true);
34552     }
34553 };
34554
34555 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34556     /**
34557      * @cfg {Function} handler
34558      * A function that will handle the click event of this menu item (defaults to undefined)
34559      */
34560     /**
34561      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34562      */
34563     canActivate : false,
34564     /**
34565      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34566      */
34567     activeClass : "x-menu-item-active",
34568     /**
34569      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34570      */
34571     hideOnClick : true,
34572     /**
34573      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34574      */
34575     hideDelay : 100,
34576
34577     // private
34578     ctype: "Roo.menu.BaseItem",
34579
34580     // private
34581     actionMode : "container",
34582
34583     // private
34584     render : function(container, parentMenu){
34585         this.parentMenu = parentMenu;
34586         Roo.menu.BaseItem.superclass.render.call(this, container);
34587         this.container.menuItemId = this.id;
34588     },
34589
34590     // private
34591     onRender : function(container, position){
34592         this.el = Roo.get(this.el);
34593         container.dom.appendChild(this.el.dom);
34594     },
34595
34596     // private
34597     onClick : function(e){
34598         if(!this.disabled && this.fireEvent("click", this, e) !== false
34599                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34600             this.handleClick(e);
34601         }else{
34602             e.stopEvent();
34603         }
34604     },
34605
34606     // private
34607     activate : function(){
34608         if(this.disabled){
34609             return false;
34610         }
34611         var li = this.container;
34612         li.addClass(this.activeClass);
34613         this.region = li.getRegion().adjust(2, 2, -2, -2);
34614         this.fireEvent("activate", this);
34615         return true;
34616     },
34617
34618     // private
34619     deactivate : function(){
34620         this.container.removeClass(this.activeClass);
34621         this.fireEvent("deactivate", this);
34622     },
34623
34624     // private
34625     shouldDeactivate : function(e){
34626         return !this.region || !this.region.contains(e.getPoint());
34627     },
34628
34629     // private
34630     handleClick : function(e){
34631         if(this.hideOnClick){
34632             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34633         }
34634     },
34635
34636     // private
34637     expandMenu : function(autoActivate){
34638         // do nothing
34639     },
34640
34641     // private
34642     hideMenu : function(){
34643         // do nothing
34644     }
34645 });/*
34646  * Based on:
34647  * Ext JS Library 1.1.1
34648  * Copyright(c) 2006-2007, Ext JS, LLC.
34649  *
34650  * Originally Released Under LGPL - original licence link has changed is not relivant.
34651  *
34652  * Fork - LGPL
34653  * <script type="text/javascript">
34654  */
34655  
34656 /**
34657  * @class Roo.menu.Adapter
34658  * @extends Roo.menu.BaseItem
34659  * 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.
34660  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34661  * @constructor
34662  * Creates a new Adapter
34663  * @param {Object} config Configuration options
34664  */
34665 Roo.menu.Adapter = function(component, config){
34666     Roo.menu.Adapter.superclass.constructor.call(this, config);
34667     this.component = component;
34668 };
34669 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34670     // private
34671     canActivate : true,
34672
34673     // private
34674     onRender : function(container, position){
34675         this.component.render(container);
34676         this.el = this.component.getEl();
34677     },
34678
34679     // private
34680     activate : function(){
34681         if(this.disabled){
34682             return false;
34683         }
34684         this.component.focus();
34685         this.fireEvent("activate", this);
34686         return true;
34687     },
34688
34689     // private
34690     deactivate : function(){
34691         this.fireEvent("deactivate", this);
34692     },
34693
34694     // private
34695     disable : function(){
34696         this.component.disable();
34697         Roo.menu.Adapter.superclass.disable.call(this);
34698     },
34699
34700     // private
34701     enable : function(){
34702         this.component.enable();
34703         Roo.menu.Adapter.superclass.enable.call(this);
34704     }
34705 });/*
34706  * Based on:
34707  * Ext JS Library 1.1.1
34708  * Copyright(c) 2006-2007, Ext JS, LLC.
34709  *
34710  * Originally Released Under LGPL - original licence link has changed is not relivant.
34711  *
34712  * Fork - LGPL
34713  * <script type="text/javascript">
34714  */
34715
34716 /**
34717  * @class Roo.menu.TextItem
34718  * @extends Roo.menu.BaseItem
34719  * Adds a static text string to a menu, usually used as either a heading or group separator.
34720  * Note: old style constructor with text is still supported.
34721  * 
34722  * @constructor
34723  * Creates a new TextItem
34724  * @param {Object} cfg Configuration
34725  */
34726 Roo.menu.TextItem = function(cfg){
34727     if (typeof(cfg) == 'string') {
34728         this.text = cfg;
34729     } else {
34730         Roo.apply(this,cfg);
34731     }
34732     
34733     Roo.menu.TextItem.superclass.constructor.call(this);
34734 };
34735
34736 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34737     /**
34738      * @cfg {Boolean} text Text to show on item.
34739      */
34740     text : '',
34741     
34742     /**
34743      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34744      */
34745     hideOnClick : false,
34746     /**
34747      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34748      */
34749     itemCls : "x-menu-text",
34750
34751     // private
34752     onRender : function(){
34753         var s = document.createElement("span");
34754         s.className = this.itemCls;
34755         s.innerHTML = this.text;
34756         this.el = s;
34757         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34758     }
34759 });/*
34760  * Based on:
34761  * Ext JS Library 1.1.1
34762  * Copyright(c) 2006-2007, Ext JS, LLC.
34763  *
34764  * Originally Released Under LGPL - original licence link has changed is not relivant.
34765  *
34766  * Fork - LGPL
34767  * <script type="text/javascript">
34768  */
34769
34770 /**
34771  * @class Roo.menu.Separator
34772  * @extends Roo.menu.BaseItem
34773  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34774  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34775  * @constructor
34776  * @param {Object} config Configuration options
34777  */
34778 Roo.menu.Separator = function(config){
34779     Roo.menu.Separator.superclass.constructor.call(this, config);
34780 };
34781
34782 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34783     /**
34784      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34785      */
34786     itemCls : "x-menu-sep",
34787     /**
34788      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34789      */
34790     hideOnClick : false,
34791
34792     // private
34793     onRender : function(li){
34794         var s = document.createElement("span");
34795         s.className = this.itemCls;
34796         s.innerHTML = "&#160;";
34797         this.el = s;
34798         li.addClass("x-menu-sep-li");
34799         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34800     }
34801 });/*
34802  * Based on:
34803  * Ext JS Library 1.1.1
34804  * Copyright(c) 2006-2007, Ext JS, LLC.
34805  *
34806  * Originally Released Under LGPL - original licence link has changed is not relivant.
34807  *
34808  * Fork - LGPL
34809  * <script type="text/javascript">
34810  */
34811 /**
34812  * @class Roo.menu.Item
34813  * @extends Roo.menu.BaseItem
34814  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34815  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34816  * activation and click handling.
34817  * @constructor
34818  * Creates a new Item
34819  * @param {Object} config Configuration options
34820  */
34821 Roo.menu.Item = function(config){
34822     Roo.menu.Item.superclass.constructor.call(this, config);
34823     if(this.menu){
34824         this.menu = Roo.menu.MenuMgr.get(this.menu);
34825     }
34826 };
34827 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34828     
34829     /**
34830      * @cfg {String} text
34831      * The text to show on the menu item.
34832      */
34833     text: '',
34834      /**
34835      * @cfg {String} HTML to render in menu
34836      * The text to show on the menu item (HTML version).
34837      */
34838     html: '',
34839     /**
34840      * @cfg {String} icon
34841      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34842      */
34843     icon: undefined,
34844     /**
34845      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34846      */
34847     itemCls : "x-menu-item",
34848     /**
34849      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34850      */
34851     canActivate : true,
34852     /**
34853      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34854      */
34855     showDelay: 200,
34856     // doc'd in BaseItem
34857     hideDelay: 200,
34858
34859     // private
34860     ctype: "Roo.menu.Item",
34861     
34862     // private
34863     onRender : function(container, position){
34864         var el = document.createElement("a");
34865         el.hideFocus = true;
34866         el.unselectable = "on";
34867         el.href = this.href || "#";
34868         if(this.hrefTarget){
34869             el.target = this.hrefTarget;
34870         }
34871         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34872         
34873         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34874         
34875         el.innerHTML = String.format(
34876                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34877                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34878         this.el = el;
34879         Roo.menu.Item.superclass.onRender.call(this, container, position);
34880     },
34881
34882     /**
34883      * Sets the text to display in this menu item
34884      * @param {String} text The text to display
34885      * @param {Boolean} isHTML true to indicate text is pure html.
34886      */
34887     setText : function(text, isHTML){
34888         if (isHTML) {
34889             this.html = text;
34890         } else {
34891             this.text = text;
34892             this.html = '';
34893         }
34894         if(this.rendered){
34895             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34896      
34897             this.el.update(String.format(
34898                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34899                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34900             this.parentMenu.autoWidth();
34901         }
34902     },
34903
34904     // private
34905     handleClick : function(e){
34906         if(!this.href){ // if no link defined, stop the event automatically
34907             e.stopEvent();
34908         }
34909         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34910     },
34911
34912     // private
34913     activate : function(autoExpand){
34914         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34915             this.focus();
34916             if(autoExpand){
34917                 this.expandMenu();
34918             }
34919         }
34920         return true;
34921     },
34922
34923     // private
34924     shouldDeactivate : function(e){
34925         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34926             if(this.menu && this.menu.isVisible()){
34927                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34928             }
34929             return true;
34930         }
34931         return false;
34932     },
34933
34934     // private
34935     deactivate : function(){
34936         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34937         this.hideMenu();
34938     },
34939
34940     // private
34941     expandMenu : function(autoActivate){
34942         if(!this.disabled && this.menu){
34943             clearTimeout(this.hideTimer);
34944             delete this.hideTimer;
34945             if(!this.menu.isVisible() && !this.showTimer){
34946                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34947             }else if (this.menu.isVisible() && autoActivate){
34948                 this.menu.tryActivate(0, 1);
34949             }
34950         }
34951     },
34952
34953     // private
34954     deferExpand : function(autoActivate){
34955         delete this.showTimer;
34956         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34957         if(autoActivate){
34958             this.menu.tryActivate(0, 1);
34959         }
34960     },
34961
34962     // private
34963     hideMenu : function(){
34964         clearTimeout(this.showTimer);
34965         delete this.showTimer;
34966         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34967             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34968         }
34969     },
34970
34971     // private
34972     deferHide : function(){
34973         delete this.hideTimer;
34974         this.menu.hide();
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.CheckItem
34989  * @extends Roo.menu.Item
34990  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34991  * @constructor
34992  * Creates a new CheckItem
34993  * @param {Object} config Configuration options
34994  */
34995 Roo.menu.CheckItem = function(config){
34996     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34997     this.addEvents({
34998         /**
34999          * @event beforecheckchange
35000          * Fires before the checked value is set, providing an opportunity to cancel if needed
35001          * @param {Roo.menu.CheckItem} this
35002          * @param {Boolean} checked The new checked value that will be set
35003          */
35004         "beforecheckchange" : true,
35005         /**
35006          * @event checkchange
35007          * Fires after the checked value has been set
35008          * @param {Roo.menu.CheckItem} this
35009          * @param {Boolean} checked The checked value that was set
35010          */
35011         "checkchange" : true
35012     });
35013     if(this.checkHandler){
35014         this.on('checkchange', this.checkHandler, this.scope);
35015     }
35016 };
35017 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35018     /**
35019      * @cfg {String} group
35020      * All check items with the same group name will automatically be grouped into a single-select
35021      * radio button group (defaults to '')
35022      */
35023     /**
35024      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35025      */
35026     itemCls : "x-menu-item x-menu-check-item",
35027     /**
35028      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35029      */
35030     groupClass : "x-menu-group-item",
35031
35032     /**
35033      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35034      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35035      * initialized with checked = true will be rendered as checked.
35036      */
35037     checked: false,
35038
35039     // private
35040     ctype: "Roo.menu.CheckItem",
35041
35042     // private
35043     onRender : function(c){
35044         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35045         if(this.group){
35046             this.el.addClass(this.groupClass);
35047         }
35048         Roo.menu.MenuMgr.registerCheckable(this);
35049         if(this.checked){
35050             this.checked = false;
35051             this.setChecked(true, true);
35052         }
35053     },
35054
35055     // private
35056     destroy : function(){
35057         if(this.rendered){
35058             Roo.menu.MenuMgr.unregisterCheckable(this);
35059         }
35060         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35061     },
35062
35063     /**
35064      * Set the checked state of this item
35065      * @param {Boolean} checked The new checked value
35066      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35067      */
35068     setChecked : function(state, suppressEvent){
35069         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35070             if(this.container){
35071                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35072             }
35073             this.checked = state;
35074             if(suppressEvent !== true){
35075                 this.fireEvent("checkchange", this, state);
35076             }
35077         }
35078     },
35079
35080     // private
35081     handleClick : function(e){
35082        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35083            this.setChecked(!this.checked);
35084        }
35085        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35086     }
35087 });/*
35088  * Based on:
35089  * Ext JS Library 1.1.1
35090  * Copyright(c) 2006-2007, Ext JS, LLC.
35091  *
35092  * Originally Released Under LGPL - original licence link has changed is not relivant.
35093  *
35094  * Fork - LGPL
35095  * <script type="text/javascript">
35096  */
35097  
35098 /**
35099  * @class Roo.menu.DateItem
35100  * @extends Roo.menu.Adapter
35101  * A menu item that wraps the {@link Roo.DatPicker} component.
35102  * @constructor
35103  * Creates a new DateItem
35104  * @param {Object} config Configuration options
35105  */
35106 Roo.menu.DateItem = function(config){
35107     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35108     /** The Roo.DatePicker object @type Roo.DatePicker */
35109     this.picker = this.component;
35110     this.addEvents({select: true});
35111     
35112     this.picker.on("render", function(picker){
35113         picker.getEl().swallowEvent("click");
35114         picker.container.addClass("x-menu-date-item");
35115     });
35116
35117     this.picker.on("select", this.onSelect, this);
35118 };
35119
35120 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35121     // private
35122     onSelect : function(picker, date){
35123         this.fireEvent("select", this, date, picker);
35124         Roo.menu.DateItem.superclass.handleClick.call(this);
35125     }
35126 });/*
35127  * Based on:
35128  * Ext JS Library 1.1.1
35129  * Copyright(c) 2006-2007, Ext JS, LLC.
35130  *
35131  * Originally Released Under LGPL - original licence link has changed is not relivant.
35132  *
35133  * Fork - LGPL
35134  * <script type="text/javascript">
35135  */
35136  
35137 /**
35138  * @class Roo.menu.ColorItem
35139  * @extends Roo.menu.Adapter
35140  * A menu item that wraps the {@link Roo.ColorPalette} component.
35141  * @constructor
35142  * Creates a new ColorItem
35143  * @param {Object} config Configuration options
35144  */
35145 Roo.menu.ColorItem = function(config){
35146     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35147     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35148     this.palette = this.component;
35149     this.relayEvents(this.palette, ["select"]);
35150     if(this.selectHandler){
35151         this.on('select', this.selectHandler, this.scope);
35152     }
35153 };
35154 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35155  * Based on:
35156  * Ext JS Library 1.1.1
35157  * Copyright(c) 2006-2007, Ext JS, LLC.
35158  *
35159  * Originally Released Under LGPL - original licence link has changed is not relivant.
35160  *
35161  * Fork - LGPL
35162  * <script type="text/javascript">
35163  */
35164  
35165
35166 /**
35167  * @class Roo.menu.DateMenu
35168  * @extends Roo.menu.Menu
35169  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35170  * @constructor
35171  * Creates a new DateMenu
35172  * @param {Object} config Configuration options
35173  */
35174 Roo.menu.DateMenu = function(config){
35175     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35176     this.plain = true;
35177     var di = new Roo.menu.DateItem(config);
35178     this.add(di);
35179     /**
35180      * The {@link Roo.DatePicker} instance for this DateMenu
35181      * @type DatePicker
35182      */
35183     this.picker = di.picker;
35184     /**
35185      * @event select
35186      * @param {DatePicker} picker
35187      * @param {Date} date
35188      */
35189     this.relayEvents(di, ["select"]);
35190
35191     this.on('beforeshow', function(){
35192         if(this.picker){
35193             this.picker.hideMonthPicker(true);
35194         }
35195     }, this);
35196 };
35197 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35198     cls:'x-date-menu'
35199 });/*
35200  * Based on:
35201  * Ext JS Library 1.1.1
35202  * Copyright(c) 2006-2007, Ext JS, LLC.
35203  *
35204  * Originally Released Under LGPL - original licence link has changed is not relivant.
35205  *
35206  * Fork - LGPL
35207  * <script type="text/javascript">
35208  */
35209  
35210
35211 /**
35212  * @class Roo.menu.ColorMenu
35213  * @extends Roo.menu.Menu
35214  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35215  * @constructor
35216  * Creates a new ColorMenu
35217  * @param {Object} config Configuration options
35218  */
35219 Roo.menu.ColorMenu = function(config){
35220     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35221     this.plain = true;
35222     var ci = new Roo.menu.ColorItem(config);
35223     this.add(ci);
35224     /**
35225      * The {@link Roo.ColorPalette} instance for this ColorMenu
35226      * @type ColorPalette
35227      */
35228     this.palette = ci.palette;
35229     /**
35230      * @event select
35231      * @param {ColorPalette} palette
35232      * @param {String} color
35233      */
35234     this.relayEvents(ci, ["select"]);
35235 };
35236 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35237  * Based on:
35238  * Ext JS Library 1.1.1
35239  * Copyright(c) 2006-2007, Ext JS, LLC.
35240  *
35241  * Originally Released Under LGPL - original licence link has changed is not relivant.
35242  *
35243  * Fork - LGPL
35244  * <script type="text/javascript">
35245  */
35246  
35247 /**
35248  * @class Roo.form.Field
35249  * @extends Roo.BoxComponent
35250  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35251  * @constructor
35252  * Creates a new Field
35253  * @param {Object} config Configuration options
35254  */
35255 Roo.form.Field = function(config){
35256     Roo.form.Field.superclass.constructor.call(this, config);
35257 };
35258
35259 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35260     /**
35261      * @cfg {String} fieldLabel Label to use when rendering a form.
35262      */
35263        /**
35264      * @cfg {String} qtip Mouse over tip
35265      */
35266      
35267     /**
35268      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35269      */
35270     invalidClass : "x-form-invalid",
35271     /**
35272      * @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")
35273      */
35274     invalidText : "The value in this field is invalid",
35275     /**
35276      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35277      */
35278     focusClass : "x-form-focus",
35279     /**
35280      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35281       automatic validation (defaults to "keyup").
35282      */
35283     validationEvent : "keyup",
35284     /**
35285      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35286      */
35287     validateOnBlur : true,
35288     /**
35289      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35290      */
35291     validationDelay : 250,
35292     /**
35293      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35294      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35295      */
35296     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35297     /**
35298      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35299      */
35300     fieldClass : "x-form-field",
35301     /**
35302      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35303      *<pre>
35304 Value         Description
35305 -----------   ----------------------------------------------------------------------
35306 qtip          Display a quick tip when the user hovers over the field
35307 title         Display a default browser title attribute popup
35308 under         Add a block div beneath the field containing the error text
35309 side          Add an error icon to the right of the field with a popup on hover
35310 [element id]  Add the error text directly to the innerHTML of the specified element
35311 </pre>
35312      */
35313     msgTarget : 'qtip',
35314     /**
35315      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35316      */
35317     msgFx : 'normal',
35318
35319     /**
35320      * @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.
35321      */
35322     readOnly : false,
35323
35324     /**
35325      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35326      */
35327     disabled : false,
35328
35329     /**
35330      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35331      */
35332     inputType : undefined,
35333     
35334     /**
35335      * @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).
35336          */
35337         tabIndex : undefined,
35338         
35339     // private
35340     isFormField : true,
35341
35342     // private
35343     hasFocus : false,
35344     /**
35345      * @property {Roo.Element} fieldEl
35346      * Element Containing the rendered Field (with label etc.)
35347      */
35348     /**
35349      * @cfg {Mixed} value A value to initialize this field with.
35350      */
35351     value : undefined,
35352
35353     /**
35354      * @cfg {String} name The field's HTML name attribute.
35355      */
35356     /**
35357      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35358      */
35359
35360         // private ??
35361         initComponent : function(){
35362         Roo.form.Field.superclass.initComponent.call(this);
35363         this.addEvents({
35364             /**
35365              * @event focus
35366              * Fires when this field receives input focus.
35367              * @param {Roo.form.Field} this
35368              */
35369             focus : true,
35370             /**
35371              * @event blur
35372              * Fires when this field loses input focus.
35373              * @param {Roo.form.Field} this
35374              */
35375             blur : true,
35376             /**
35377              * @event specialkey
35378              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35379              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35380              * @param {Roo.form.Field} this
35381              * @param {Roo.EventObject} e The event object
35382              */
35383             specialkey : true,
35384             /**
35385              * @event change
35386              * Fires just before the field blurs if the field value has changed.
35387              * @param {Roo.form.Field} this
35388              * @param {Mixed} newValue The new value
35389              * @param {Mixed} oldValue The original value
35390              */
35391             change : true,
35392             /**
35393              * @event invalid
35394              * Fires after the field has been marked as invalid.
35395              * @param {Roo.form.Field} this
35396              * @param {String} msg The validation message
35397              */
35398             invalid : true,
35399             /**
35400              * @event valid
35401              * Fires after the field has been validated with no errors.
35402              * @param {Roo.form.Field} this
35403              */
35404             valid : true,
35405              /**
35406              * @event keyup
35407              * Fires after the key up
35408              * @param {Roo.form.Field} this
35409              * @param {Roo.EventObject}  e The event Object
35410              */
35411             keyup : true
35412         });
35413     },
35414
35415     /**
35416      * Returns the name attribute of the field if available
35417      * @return {String} name The field name
35418      */
35419     getName: function(){
35420          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35421     },
35422
35423     // private
35424     onRender : function(ct, position){
35425         Roo.form.Field.superclass.onRender.call(this, ct, position);
35426         if(!this.el){
35427             var cfg = this.getAutoCreate();
35428             if(!cfg.name){
35429                 cfg.name = this.name || this.id;
35430             }
35431             if(this.inputType){
35432                 cfg.type = this.inputType;
35433             }
35434             this.el = ct.createChild(cfg, position);
35435         }
35436         var type = this.el.dom.type;
35437         if(type){
35438             if(type == 'password'){
35439                 type = 'text';
35440             }
35441             this.el.addClass('x-form-'+type);
35442         }
35443         if(this.readOnly){
35444             this.el.dom.readOnly = true;
35445         }
35446         if(this.tabIndex !== undefined){
35447             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35448         }
35449
35450         this.el.addClass([this.fieldClass, this.cls]);
35451         this.initValue();
35452     },
35453
35454     /**
35455      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35456      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35457      * @return {Roo.form.Field} this
35458      */
35459     applyTo : function(target){
35460         this.allowDomMove = false;
35461         this.el = Roo.get(target);
35462         this.render(this.el.dom.parentNode);
35463         return this;
35464     },
35465
35466     // private
35467     initValue : function(){
35468         if(this.value !== undefined){
35469             this.setValue(this.value);
35470         }else if(this.el.dom.value.length > 0){
35471             this.setValue(this.el.dom.value);
35472         }
35473     },
35474
35475     /**
35476      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35477      */
35478     isDirty : function() {
35479         if(this.disabled) {
35480             return false;
35481         }
35482         return String(this.getValue()) !== String(this.originalValue);
35483     },
35484
35485     // private
35486     afterRender : function(){
35487         Roo.form.Field.superclass.afterRender.call(this);
35488         this.initEvents();
35489     },
35490
35491     // private
35492     fireKey : function(e){
35493         //Roo.log('field ' + e.getKey());
35494         if(e.isNavKeyPress()){
35495             this.fireEvent("specialkey", this, e);
35496         }
35497     },
35498
35499     /**
35500      * Resets the current field value to the originally loaded value and clears any validation messages
35501      */
35502     reset : function(){
35503         this.setValue(this.originalValue);
35504         this.clearInvalid();
35505     },
35506
35507     // private
35508     initEvents : function(){
35509         // safari killled keypress - so keydown is now used..
35510         this.el.on("keydown" , this.fireKey,  this);
35511         this.el.on("focus", this.onFocus,  this);
35512         this.el.on("blur", this.onBlur,  this);
35513         this.el.relayEvent('keyup', this);
35514
35515         // reference to original value for reset
35516         this.originalValue = this.getValue();
35517     },
35518
35519     // private
35520     onFocus : function(){
35521         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35522             this.el.addClass(this.focusClass);
35523         }
35524         if(!this.hasFocus){
35525             this.hasFocus = true;
35526             this.startValue = this.getValue();
35527             this.fireEvent("focus", this);
35528         }
35529     },
35530
35531     beforeBlur : Roo.emptyFn,
35532
35533     // private
35534     onBlur : function(){
35535         this.beforeBlur();
35536         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35537             this.el.removeClass(this.focusClass);
35538         }
35539         this.hasFocus = false;
35540         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35541             this.validate();
35542         }
35543         var v = this.getValue();
35544         if(String(v) !== String(this.startValue)){
35545             this.fireEvent('change', this, v, this.startValue);
35546         }
35547         this.fireEvent("blur", this);
35548     },
35549
35550     /**
35551      * Returns whether or not the field value is currently valid
35552      * @param {Boolean} preventMark True to disable marking the field invalid
35553      * @return {Boolean} True if the value is valid, else false
35554      */
35555     isValid : function(preventMark){
35556         if(this.disabled){
35557             return true;
35558         }
35559         var restore = this.preventMark;
35560         this.preventMark = preventMark === true;
35561         var v = this.validateValue(this.processValue(this.getRawValue()));
35562         this.preventMark = restore;
35563         return v;
35564     },
35565
35566     /**
35567      * Validates the field value
35568      * @return {Boolean} True if the value is valid, else false
35569      */
35570     validate : function(){
35571         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35572             this.clearInvalid();
35573             return true;
35574         }
35575         return false;
35576     },
35577
35578     processValue : function(value){
35579         return value;
35580     },
35581
35582     // private
35583     // Subclasses should provide the validation implementation by overriding this
35584     validateValue : function(value){
35585         return true;
35586     },
35587
35588     /**
35589      * Mark this field as invalid
35590      * @param {String} msg The validation message
35591      */
35592     markInvalid : function(msg){
35593         if(!this.rendered || this.preventMark){ // not rendered
35594             return;
35595         }
35596         this.el.addClass(this.invalidClass);
35597         msg = msg || this.invalidText;
35598         switch(this.msgTarget){
35599             case 'qtip':
35600                 this.el.dom.qtip = msg;
35601                 this.el.dom.qclass = 'x-form-invalid-tip';
35602                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35603                     Roo.QuickTips.enable();
35604                 }
35605                 break;
35606             case 'title':
35607                 this.el.dom.title = msg;
35608                 break;
35609             case 'under':
35610                 if(!this.errorEl){
35611                     var elp = this.el.findParent('.x-form-element', 5, true);
35612                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35613                     this.errorEl.setWidth(elp.getWidth(true)-20);
35614                 }
35615                 this.errorEl.update(msg);
35616                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35617                 break;
35618             case 'side':
35619                 if(!this.errorIcon){
35620                     var elp = this.el.findParent('.x-form-element', 5, true);
35621                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35622                 }
35623                 this.alignErrorIcon();
35624                 this.errorIcon.dom.qtip = msg;
35625                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35626                 this.errorIcon.show();
35627                 this.on('resize', this.alignErrorIcon, this);
35628                 break;
35629             default:
35630                 var t = Roo.getDom(this.msgTarget);
35631                 t.innerHTML = msg;
35632                 t.style.display = this.msgDisplay;
35633                 break;
35634         }
35635         this.fireEvent('invalid', this, msg);
35636     },
35637
35638     // private
35639     alignErrorIcon : function(){
35640         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35641     },
35642
35643     /**
35644      * Clear any invalid styles/messages for this field
35645      */
35646     clearInvalid : function(){
35647         if(!this.rendered || this.preventMark){ // not rendered
35648             return;
35649         }
35650         this.el.removeClass(this.invalidClass);
35651         switch(this.msgTarget){
35652             case 'qtip':
35653                 this.el.dom.qtip = '';
35654                 break;
35655             case 'title':
35656                 this.el.dom.title = '';
35657                 break;
35658             case 'under':
35659                 if(this.errorEl){
35660                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35661                 }
35662                 break;
35663             case 'side':
35664                 if(this.errorIcon){
35665                     this.errorIcon.dom.qtip = '';
35666                     this.errorIcon.hide();
35667                     this.un('resize', this.alignErrorIcon, this);
35668                 }
35669                 break;
35670             default:
35671                 var t = Roo.getDom(this.msgTarget);
35672                 t.innerHTML = '';
35673                 t.style.display = 'none';
35674                 break;
35675         }
35676         this.fireEvent('valid', this);
35677     },
35678
35679     /**
35680      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35681      * @return {Mixed} value The field value
35682      */
35683     getRawValue : function(){
35684         var v = this.el.getValue();
35685         if(v === this.emptyText){
35686             v = '';
35687         }
35688         return v;
35689     },
35690
35691     /**
35692      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35693      * @return {Mixed} value The field value
35694      */
35695     getValue : function(){
35696         var v = this.el.getValue();
35697         if(v === this.emptyText || v === undefined){
35698             v = '';
35699         }
35700         return v;
35701     },
35702
35703     /**
35704      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35705      * @param {Mixed} value The value to set
35706      */
35707     setRawValue : function(v){
35708         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35709     },
35710
35711     /**
35712      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35713      * @param {Mixed} value The value to set
35714      */
35715     setValue : function(v){
35716         this.value = v;
35717         if(this.rendered){
35718             this.el.dom.value = (v === null || v === undefined ? '' : v);
35719             this.validate();
35720         }
35721     },
35722
35723     adjustSize : function(w, h){
35724         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35725         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35726         return s;
35727     },
35728
35729     adjustWidth : function(tag, w){
35730         tag = tag.toLowerCase();
35731         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35732             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35733                 if(tag == 'input'){
35734                     return w + 2;
35735                 }
35736                 if(tag = 'textarea'){
35737                     return w-2;
35738                 }
35739             }else if(Roo.isOpera){
35740                 if(tag == 'input'){
35741                     return w + 2;
35742                 }
35743                 if(tag = 'textarea'){
35744                     return w-2;
35745                 }
35746             }
35747         }
35748         return w;
35749     }
35750 });
35751
35752
35753 // anything other than normal should be considered experimental
35754 Roo.form.Field.msgFx = {
35755     normal : {
35756         show: function(msgEl, f){
35757             msgEl.setDisplayed('block');
35758         },
35759
35760         hide : function(msgEl, f){
35761             msgEl.setDisplayed(false).update('');
35762         }
35763     },
35764
35765     slide : {
35766         show: function(msgEl, f){
35767             msgEl.slideIn('t', {stopFx:true});
35768         },
35769
35770         hide : function(msgEl, f){
35771             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35772         }
35773     },
35774
35775     slideRight : {
35776         show: function(msgEl, f){
35777             msgEl.fixDisplay();
35778             msgEl.alignTo(f.el, 'tl-tr');
35779             msgEl.slideIn('l', {stopFx:true});
35780         },
35781
35782         hide : function(msgEl, f){
35783             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35784         }
35785     }
35786 };/*
35787  * Based on:
35788  * Ext JS Library 1.1.1
35789  * Copyright(c) 2006-2007, Ext JS, LLC.
35790  *
35791  * Originally Released Under LGPL - original licence link has changed is not relivant.
35792  *
35793  * Fork - LGPL
35794  * <script type="text/javascript">
35795  */
35796  
35797
35798 /**
35799  * @class Roo.form.TextField
35800  * @extends Roo.form.Field
35801  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35802  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35803  * @constructor
35804  * Creates a new TextField
35805  * @param {Object} config Configuration options
35806  */
35807 Roo.form.TextField = function(config){
35808     Roo.form.TextField.superclass.constructor.call(this, config);
35809     this.addEvents({
35810         /**
35811          * @event autosize
35812          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35813          * according to the default logic, but this event provides a hook for the developer to apply additional
35814          * logic at runtime to resize the field if needed.
35815              * @param {Roo.form.Field} this This text field
35816              * @param {Number} width The new field width
35817              */
35818         autosize : true
35819     });
35820 };
35821
35822 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35823     /**
35824      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35825      */
35826     grow : false,
35827     /**
35828      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35829      */
35830     growMin : 30,
35831     /**
35832      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35833      */
35834     growMax : 800,
35835     /**
35836      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35837      */
35838     vtype : null,
35839     /**
35840      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35841      */
35842     maskRe : null,
35843     /**
35844      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35845      */
35846     disableKeyFilter : false,
35847     /**
35848      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35849      */
35850     allowBlank : true,
35851     /**
35852      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35853      */
35854     minLength : 0,
35855     /**
35856      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35857      */
35858     maxLength : Number.MAX_VALUE,
35859     /**
35860      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35861      */
35862     minLengthText : "The minimum length for this field is {0}",
35863     /**
35864      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35865      */
35866     maxLengthText : "The maximum length for this field is {0}",
35867     /**
35868      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35869      */
35870     selectOnFocus : false,
35871     /**
35872      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35873      */
35874     blankText : "This field is required",
35875     /**
35876      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35877      * If available, this function will be called only after the basic validators all return true, and will be passed the
35878      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35879      */
35880     validator : null,
35881     /**
35882      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35883      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35884      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35885      */
35886     regex : null,
35887     /**
35888      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35889      */
35890     regexText : "",
35891     /**
35892      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35893      */
35894     emptyText : null,
35895     /**
35896      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35897      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35898      */
35899     emptyClass : 'x-form-empty-field',
35900
35901     // private
35902     initEvents : function(){
35903         Roo.form.TextField.superclass.initEvents.call(this);
35904         if(this.validationEvent == 'keyup'){
35905             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35906             this.el.on('keyup', this.filterValidation, this);
35907         }
35908         else if(this.validationEvent !== false){
35909             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35910         }
35911         if(this.selectOnFocus || this.emptyText){
35912             this.on("focus", this.preFocus, this);
35913             if(this.emptyText){
35914                 this.on('blur', this.postBlur, this);
35915                 this.applyEmptyText();
35916             }
35917         }
35918         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35919             this.el.on("keypress", this.filterKeys, this);
35920         }
35921         if(this.grow){
35922             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35923             this.el.on("click", this.autoSize,  this);
35924         }
35925     },
35926
35927     processValue : function(value){
35928         if(this.stripCharsRe){
35929             var newValue = value.replace(this.stripCharsRe, '');
35930             if(newValue !== value){
35931                 this.setRawValue(newValue);
35932                 return newValue;
35933             }
35934         }
35935         return value;
35936     },
35937
35938     filterValidation : function(e){
35939         if(!e.isNavKeyPress()){
35940             this.validationTask.delay(this.validationDelay);
35941         }
35942     },
35943
35944     // private
35945     onKeyUp : function(e){
35946         if(!e.isNavKeyPress()){
35947             this.autoSize();
35948         }
35949     },
35950
35951     /**
35952      * Resets the current field value to the originally-loaded value and clears any validation messages.
35953      * Also adds emptyText and emptyClass if the original value was blank.
35954      */
35955     reset : function(){
35956         Roo.form.TextField.superclass.reset.call(this);
35957         this.applyEmptyText();
35958     },
35959
35960     applyEmptyText : function(){
35961         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35962             this.setRawValue(this.emptyText);
35963             this.el.addClass(this.emptyClass);
35964         }
35965     },
35966
35967     // private
35968     preFocus : function(){
35969         if(this.emptyText){
35970             if(this.el.dom.value == this.emptyText){
35971                 this.setRawValue('');
35972             }
35973             this.el.removeClass(this.emptyClass);
35974         }
35975         if(this.selectOnFocus){
35976             this.el.dom.select();
35977         }
35978     },
35979
35980     // private
35981     postBlur : function(){
35982         this.applyEmptyText();
35983     },
35984
35985     // private
35986     filterKeys : function(e){
35987         var k = e.getKey();
35988         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35989             return;
35990         }
35991         var c = e.getCharCode(), cc = String.fromCharCode(c);
35992         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35993             return;
35994         }
35995         if(!this.maskRe.test(cc)){
35996             e.stopEvent();
35997         }
35998     },
35999
36000     setValue : function(v){
36001         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36002             this.el.removeClass(this.emptyClass);
36003         }
36004         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36005         this.applyEmptyText();
36006         this.autoSize();
36007     },
36008
36009     /**
36010      * Validates a value according to the field's validation rules and marks the field as invalid
36011      * if the validation fails
36012      * @param {Mixed} value The value to validate
36013      * @return {Boolean} True if the value is valid, else false
36014      */
36015     validateValue : function(value){
36016         if(value.length < 1 || value === this.emptyText){ // if it's blank
36017              if(this.allowBlank){
36018                 this.clearInvalid();
36019                 return true;
36020              }else{
36021                 this.markInvalid(this.blankText);
36022                 return false;
36023              }
36024         }
36025         if(value.length < this.minLength){
36026             this.markInvalid(String.format(this.minLengthText, this.minLength));
36027             return false;
36028         }
36029         if(value.length > this.maxLength){
36030             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36031             return false;
36032         }
36033         if(this.vtype){
36034             var vt = Roo.form.VTypes;
36035             if(!vt[this.vtype](value, this)){
36036                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36037                 return false;
36038             }
36039         }
36040         if(typeof this.validator == "function"){
36041             var msg = this.validator(value);
36042             if(msg !== true){
36043                 this.markInvalid(msg);
36044                 return false;
36045             }
36046         }
36047         if(this.regex && !this.regex.test(value)){
36048             this.markInvalid(this.regexText);
36049             return false;
36050         }
36051         return true;
36052     },
36053
36054     /**
36055      * Selects text in this field
36056      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36057      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36058      */
36059     selectText : function(start, end){
36060         var v = this.getRawValue();
36061         if(v.length > 0){
36062             start = start === undefined ? 0 : start;
36063             end = end === undefined ? v.length : end;
36064             var d = this.el.dom;
36065             if(d.setSelectionRange){
36066                 d.setSelectionRange(start, end);
36067             }else if(d.createTextRange){
36068                 var range = d.createTextRange();
36069                 range.moveStart("character", start);
36070                 range.moveEnd("character", v.length-end);
36071                 range.select();
36072             }
36073         }
36074     },
36075
36076     /**
36077      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36078      * This only takes effect if grow = true, and fires the autosize event.
36079      */
36080     autoSize : function(){
36081         if(!this.grow || !this.rendered){
36082             return;
36083         }
36084         if(!this.metrics){
36085             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36086         }
36087         var el = this.el;
36088         var v = el.dom.value;
36089         var d = document.createElement('div');
36090         d.appendChild(document.createTextNode(v));
36091         v = d.innerHTML;
36092         d = null;
36093         v += "&#160;";
36094         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36095         this.el.setWidth(w);
36096         this.fireEvent("autosize", this, w);
36097     }
36098 });/*
36099  * Based on:
36100  * Ext JS Library 1.1.1
36101  * Copyright(c) 2006-2007, Ext JS, LLC.
36102  *
36103  * Originally Released Under LGPL - original licence link has changed is not relivant.
36104  *
36105  * Fork - LGPL
36106  * <script type="text/javascript">
36107  */
36108  
36109 /**
36110  * @class Roo.form.Hidden
36111  * @extends Roo.form.TextField
36112  * Simple Hidden element used on forms 
36113  * 
36114  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36115  * 
36116  * @constructor
36117  * Creates a new Hidden form element.
36118  * @param {Object} config Configuration options
36119  */
36120
36121
36122
36123 // easy hidden field...
36124 Roo.form.Hidden = function(config){
36125     Roo.form.Hidden.superclass.constructor.call(this, config);
36126 };
36127   
36128 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36129     fieldLabel:      '',
36130     inputType:      'hidden',
36131     width:          50,
36132     allowBlank:     true,
36133     labelSeparator: '',
36134     hidden:         true,
36135     itemCls :       'x-form-item-display-none'
36136
36137
36138 });
36139
36140
36141 /*
36142  * Based on:
36143  * Ext JS Library 1.1.1
36144  * Copyright(c) 2006-2007, Ext JS, LLC.
36145  *
36146  * Originally Released Under LGPL - original licence link has changed is not relivant.
36147  *
36148  * Fork - LGPL
36149  * <script type="text/javascript">
36150  */
36151  
36152 /**
36153  * @class Roo.form.TriggerField
36154  * @extends Roo.form.TextField
36155  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36156  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36157  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36158  * for which you can provide a custom implementation.  For example:
36159  * <pre><code>
36160 var trigger = new Roo.form.TriggerField();
36161 trigger.onTriggerClick = myTriggerFn;
36162 trigger.applyTo('my-field');
36163 </code></pre>
36164  *
36165  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36166  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36167  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36168  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36169  * @constructor
36170  * Create a new TriggerField.
36171  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36172  * to the base TextField)
36173  */
36174 Roo.form.TriggerField = function(config){
36175     this.mimicing = false;
36176     Roo.form.TriggerField.superclass.constructor.call(this, config);
36177 };
36178
36179 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36180     /**
36181      * @cfg {String} triggerClass A CSS class to apply to the trigger
36182      */
36183     /**
36184      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36185      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36186      */
36187     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36188     /**
36189      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36190      */
36191     hideTrigger:false,
36192
36193     /** @cfg {Boolean} grow @hide */
36194     /** @cfg {Number} growMin @hide */
36195     /** @cfg {Number} growMax @hide */
36196
36197     /**
36198      * @hide 
36199      * @method
36200      */
36201     autoSize: Roo.emptyFn,
36202     // private
36203     monitorTab : true,
36204     // private
36205     deferHeight : true,
36206
36207     
36208     actionMode : 'wrap',
36209     // private
36210     onResize : function(w, h){
36211         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36212         if(typeof w == 'number'){
36213             var x = w - this.trigger.getWidth();
36214             this.el.setWidth(this.adjustWidth('input', x));
36215             this.trigger.setStyle('left', x+'px');
36216         }
36217     },
36218
36219     // private
36220     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36221
36222     // private
36223     getResizeEl : function(){
36224         return this.wrap;
36225     },
36226
36227     // private
36228     getPositionEl : function(){
36229         return this.wrap;
36230     },
36231
36232     // private
36233     alignErrorIcon : function(){
36234         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36235     },
36236
36237     // private
36238     onRender : function(ct, position){
36239         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36240         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36241         this.trigger = this.wrap.createChild(this.triggerConfig ||
36242                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36243         if(this.hideTrigger){
36244             this.trigger.setDisplayed(false);
36245         }
36246         this.initTrigger();
36247         if(!this.width){
36248             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36249         }
36250     },
36251
36252     // private
36253     initTrigger : function(){
36254         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36255         this.trigger.addClassOnOver('x-form-trigger-over');
36256         this.trigger.addClassOnClick('x-form-trigger-click');
36257     },
36258
36259     // private
36260     onDestroy : function(){
36261         if(this.trigger){
36262             this.trigger.removeAllListeners();
36263             this.trigger.remove();
36264         }
36265         if(this.wrap){
36266             this.wrap.remove();
36267         }
36268         Roo.form.TriggerField.superclass.onDestroy.call(this);
36269     },
36270
36271     // private
36272     onFocus : function(){
36273         Roo.form.TriggerField.superclass.onFocus.call(this);
36274         if(!this.mimicing){
36275             this.wrap.addClass('x-trigger-wrap-focus');
36276             this.mimicing = true;
36277             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36278             if(this.monitorTab){
36279                 this.el.on("keydown", this.checkTab, this);
36280             }
36281         }
36282     },
36283
36284     // private
36285     checkTab : function(e){
36286         if(e.getKey() == e.TAB){
36287             this.triggerBlur();
36288         }
36289     },
36290
36291     // private
36292     onBlur : function(){
36293         // do nothing
36294     },
36295
36296     // private
36297     mimicBlur : function(e, t){
36298         if(!this.wrap.contains(t) && this.validateBlur()){
36299             this.triggerBlur();
36300         }
36301     },
36302
36303     // private
36304     triggerBlur : function(){
36305         this.mimicing = false;
36306         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36307         if(this.monitorTab){
36308             this.el.un("keydown", this.checkTab, this);
36309         }
36310         this.wrap.removeClass('x-trigger-wrap-focus');
36311         Roo.form.TriggerField.superclass.onBlur.call(this);
36312     },
36313
36314     // private
36315     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36316     validateBlur : function(e, t){
36317         return true;
36318     },
36319
36320     // private
36321     onDisable : function(){
36322         Roo.form.TriggerField.superclass.onDisable.call(this);
36323         if(this.wrap){
36324             this.wrap.addClass('x-item-disabled');
36325         }
36326     },
36327
36328     // private
36329     onEnable : function(){
36330         Roo.form.TriggerField.superclass.onEnable.call(this);
36331         if(this.wrap){
36332             this.wrap.removeClass('x-item-disabled');
36333         }
36334     },
36335
36336     // private
36337     onShow : function(){
36338         var ae = this.getActionEl();
36339         
36340         if(ae){
36341             ae.dom.style.display = '';
36342             ae.dom.style.visibility = 'visible';
36343         }
36344     },
36345
36346     // private
36347     
36348     onHide : function(){
36349         var ae = this.getActionEl();
36350         ae.dom.style.display = 'none';
36351     },
36352
36353     /**
36354      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36355      * by an implementing function.
36356      * @method
36357      * @param {EventObject} e
36358      */
36359     onTriggerClick : Roo.emptyFn
36360 });
36361
36362 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36363 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36364 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36365 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36366     initComponent : function(){
36367         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36368
36369         this.triggerConfig = {
36370             tag:'span', cls:'x-form-twin-triggers', cn:[
36371             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36372             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36373         ]};
36374     },
36375
36376     getTrigger : function(index){
36377         return this.triggers[index];
36378     },
36379
36380     initTrigger : function(){
36381         var ts = this.trigger.select('.x-form-trigger', true);
36382         this.wrap.setStyle('overflow', 'hidden');
36383         var triggerField = this;
36384         ts.each(function(t, all, index){
36385             t.hide = function(){
36386                 var w = triggerField.wrap.getWidth();
36387                 this.dom.style.display = 'none';
36388                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36389             };
36390             t.show = function(){
36391                 var w = triggerField.wrap.getWidth();
36392                 this.dom.style.display = '';
36393                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36394             };
36395             var triggerIndex = 'Trigger'+(index+1);
36396
36397             if(this['hide'+triggerIndex]){
36398                 t.dom.style.display = 'none';
36399             }
36400             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36401             t.addClassOnOver('x-form-trigger-over');
36402             t.addClassOnClick('x-form-trigger-click');
36403         }, this);
36404         this.triggers = ts.elements;
36405     },
36406
36407     onTrigger1Click : Roo.emptyFn,
36408     onTrigger2Click : Roo.emptyFn
36409 });/*
36410  * Based on:
36411  * Ext JS Library 1.1.1
36412  * Copyright(c) 2006-2007, Ext JS, LLC.
36413  *
36414  * Originally Released Under LGPL - original licence link has changed is not relivant.
36415  *
36416  * Fork - LGPL
36417  * <script type="text/javascript">
36418  */
36419  
36420 /**
36421  * @class Roo.form.TextArea
36422  * @extends Roo.form.TextField
36423  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36424  * support for auto-sizing.
36425  * @constructor
36426  * Creates a new TextArea
36427  * @param {Object} config Configuration options
36428  */
36429 Roo.form.TextArea = function(config){
36430     Roo.form.TextArea.superclass.constructor.call(this, config);
36431     // these are provided exchanges for backwards compat
36432     // minHeight/maxHeight were replaced by growMin/growMax to be
36433     // compatible with TextField growing config values
36434     if(this.minHeight !== undefined){
36435         this.growMin = this.minHeight;
36436     }
36437     if(this.maxHeight !== undefined){
36438         this.growMax = this.maxHeight;
36439     }
36440 };
36441
36442 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36443     /**
36444      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36445      */
36446     growMin : 60,
36447     /**
36448      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36449      */
36450     growMax: 1000,
36451     /**
36452      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36453      * in the field (equivalent to setting overflow: hidden, defaults to false)
36454      */
36455     preventScrollbars: false,
36456     /**
36457      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36458      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36459      */
36460
36461     // private
36462     onRender : function(ct, position){
36463         if(!this.el){
36464             this.defaultAutoCreate = {
36465                 tag: "textarea",
36466                 style:"width:300px;height:60px;",
36467                 autocomplete: "off"
36468             };
36469         }
36470         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36471         if(this.grow){
36472             this.textSizeEl = Roo.DomHelper.append(document.body, {
36473                 tag: "pre", cls: "x-form-grow-sizer"
36474             });
36475             if(this.preventScrollbars){
36476                 this.el.setStyle("overflow", "hidden");
36477             }
36478             this.el.setHeight(this.growMin);
36479         }
36480     },
36481
36482     onDestroy : function(){
36483         if(this.textSizeEl){
36484             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36485         }
36486         Roo.form.TextArea.superclass.onDestroy.call(this);
36487     },
36488
36489     // private
36490     onKeyUp : function(e){
36491         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36492             this.autoSize();
36493         }
36494     },
36495
36496     /**
36497      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36498      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36499      */
36500     autoSize : function(){
36501         if(!this.grow || !this.textSizeEl){
36502             return;
36503         }
36504         var el = this.el;
36505         var v = el.dom.value;
36506         var ts = this.textSizeEl;
36507
36508         ts.innerHTML = '';
36509         ts.appendChild(document.createTextNode(v));
36510         v = ts.innerHTML;
36511
36512         Roo.fly(ts).setWidth(this.el.getWidth());
36513         if(v.length < 1){
36514             v = "&#160;&#160;";
36515         }else{
36516             if(Roo.isIE){
36517                 v = v.replace(/\n/g, '<p>&#160;</p>');
36518             }
36519             v += "&#160;\n&#160;";
36520         }
36521         ts.innerHTML = v;
36522         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36523         if(h != this.lastHeight){
36524             this.lastHeight = h;
36525             this.el.setHeight(h);
36526             this.fireEvent("autosize", this, h);
36527         }
36528     }
36529 });/*
36530  * Based on:
36531  * Ext JS Library 1.1.1
36532  * Copyright(c) 2006-2007, Ext JS, LLC.
36533  *
36534  * Originally Released Under LGPL - original licence link has changed is not relivant.
36535  *
36536  * Fork - LGPL
36537  * <script type="text/javascript">
36538  */
36539  
36540
36541 /**
36542  * @class Roo.form.NumberField
36543  * @extends Roo.form.TextField
36544  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36545  * @constructor
36546  * Creates a new NumberField
36547  * @param {Object} config Configuration options
36548  */
36549 Roo.form.NumberField = function(config){
36550     Roo.form.NumberField.superclass.constructor.call(this, config);
36551 };
36552
36553 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36554     /**
36555      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36556      */
36557     fieldClass: "x-form-field x-form-num-field",
36558     /**
36559      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36560      */
36561     allowDecimals : true,
36562     /**
36563      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36564      */
36565     decimalSeparator : ".",
36566     /**
36567      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36568      */
36569     decimalPrecision : 2,
36570     /**
36571      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36572      */
36573     allowNegative : true,
36574     /**
36575      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36576      */
36577     minValue : Number.NEGATIVE_INFINITY,
36578     /**
36579      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36580      */
36581     maxValue : Number.MAX_VALUE,
36582     /**
36583      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36584      */
36585     minText : "The minimum value for this field is {0}",
36586     /**
36587      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36588      */
36589     maxText : "The maximum value for this field is {0}",
36590     /**
36591      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36592      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36593      */
36594     nanText : "{0} is not a valid number",
36595
36596     // private
36597     initEvents : function(){
36598         Roo.form.NumberField.superclass.initEvents.call(this);
36599         var allowed = "0123456789";
36600         if(this.allowDecimals){
36601             allowed += this.decimalSeparator;
36602         }
36603         if(this.allowNegative){
36604             allowed += "-";
36605         }
36606         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36607         var keyPress = function(e){
36608             var k = e.getKey();
36609             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36610                 return;
36611             }
36612             var c = e.getCharCode();
36613             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36614                 e.stopEvent();
36615             }
36616         };
36617         this.el.on("keypress", keyPress, this);
36618     },
36619
36620     // private
36621     validateValue : function(value){
36622         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36623             return false;
36624         }
36625         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36626              return true;
36627         }
36628         var num = this.parseValue(value);
36629         if(isNaN(num)){
36630             this.markInvalid(String.format(this.nanText, value));
36631             return false;
36632         }
36633         if(num < this.minValue){
36634             this.markInvalid(String.format(this.minText, this.minValue));
36635             return false;
36636         }
36637         if(num > this.maxValue){
36638             this.markInvalid(String.format(this.maxText, this.maxValue));
36639             return false;
36640         }
36641         return true;
36642     },
36643
36644     getValue : function(){
36645         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36646     },
36647
36648     // private
36649     parseValue : function(value){
36650         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36651         return isNaN(value) ? '' : value;
36652     },
36653
36654     // private
36655     fixPrecision : function(value){
36656         var nan = isNaN(value);
36657         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36658             return nan ? '' : value;
36659         }
36660         return parseFloat(value).toFixed(this.decimalPrecision);
36661     },
36662
36663     setValue : function(v){
36664         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36665     },
36666
36667     // private
36668     decimalPrecisionFcn : function(v){
36669         return Math.floor(v);
36670     },
36671
36672     beforeBlur : function(){
36673         var v = this.parseValue(this.getRawValue());
36674         if(v){
36675             this.setValue(this.fixPrecision(v));
36676         }
36677     }
36678 });/*
36679  * Based on:
36680  * Ext JS Library 1.1.1
36681  * Copyright(c) 2006-2007, Ext JS, LLC.
36682  *
36683  * Originally Released Under LGPL - original licence link has changed is not relivant.
36684  *
36685  * Fork - LGPL
36686  * <script type="text/javascript">
36687  */
36688  
36689 /**
36690  * @class Roo.form.DateField
36691  * @extends Roo.form.TriggerField
36692  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36693 * @constructor
36694 * Create a new DateField
36695 * @param {Object} config
36696  */
36697 Roo.form.DateField = function(config){
36698     Roo.form.DateField.superclass.constructor.call(this, config);
36699     
36700       this.addEvents({
36701          
36702         /**
36703          * @event select
36704          * Fires when a date is selected
36705              * @param {Roo.form.DateField} combo This combo box
36706              * @param {Date} date The date selected
36707              */
36708         'select' : true
36709          
36710     });
36711     
36712     
36713     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36714     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36715     this.ddMatch = null;
36716     if(this.disabledDates){
36717         var dd = this.disabledDates;
36718         var re = "(?:";
36719         for(var i = 0; i < dd.length; i++){
36720             re += dd[i];
36721             if(i != dd.length-1) re += "|";
36722         }
36723         this.ddMatch = new RegExp(re + ")");
36724     }
36725 };
36726
36727 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36728     /**
36729      * @cfg {String} format
36730      * The default date format string which can be overriden for localization support.  The format must be
36731      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36732      */
36733     format : "m/d/y",
36734     /**
36735      * @cfg {String} altFormats
36736      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36737      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36738      */
36739     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36740     /**
36741      * @cfg {Array} disabledDays
36742      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36743      */
36744     disabledDays : null,
36745     /**
36746      * @cfg {String} disabledDaysText
36747      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36748      */
36749     disabledDaysText : "Disabled",
36750     /**
36751      * @cfg {Array} disabledDates
36752      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36753      * expression so they are very powerful. Some examples:
36754      * <ul>
36755      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36756      * <li>["03/08", "09/16"] would disable those days for every year</li>
36757      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36758      * <li>["03/../2006"] would disable every day in March 2006</li>
36759      * <li>["^03"] would disable every day in every March</li>
36760      * </ul>
36761      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36762      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36763      */
36764     disabledDates : null,
36765     /**
36766      * @cfg {String} disabledDatesText
36767      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36768      */
36769     disabledDatesText : "Disabled",
36770     /**
36771      * @cfg {Date/String} minValue
36772      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36773      * valid format (defaults to null).
36774      */
36775     minValue : null,
36776     /**
36777      * @cfg {Date/String} maxValue
36778      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36779      * valid format (defaults to null).
36780      */
36781     maxValue : null,
36782     /**
36783      * @cfg {String} minText
36784      * The error text to display when the date in the cell is before minValue (defaults to
36785      * 'The date in this field must be after {minValue}').
36786      */
36787     minText : "The date in this field must be equal to or after {0}",
36788     /**
36789      * @cfg {String} maxText
36790      * The error text to display when the date in the cell is after maxValue (defaults to
36791      * 'The date in this field must be before {maxValue}').
36792      */
36793     maxText : "The date in this field must be equal to or before {0}",
36794     /**
36795      * @cfg {String} invalidText
36796      * The error text to display when the date in the field is invalid (defaults to
36797      * '{value} is not a valid date - it must be in the format {format}').
36798      */
36799     invalidText : "{0} is not a valid date - it must be in the format {1}",
36800     /**
36801      * @cfg {String} triggerClass
36802      * An additional CSS class used to style the trigger button.  The trigger will always get the
36803      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36804      * which displays a calendar icon).
36805      */
36806     triggerClass : 'x-form-date-trigger',
36807     
36808
36809     /**
36810      * @cfg {bool} useIso
36811      * if enabled, then the date field will use a hidden field to store the 
36812      * real value as iso formated date. default (false)
36813      */ 
36814     useIso : false,
36815     /**
36816      * @cfg {String/Object} autoCreate
36817      * A DomHelper element spec, or true for a default element spec (defaults to
36818      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36819      */ 
36820     // private
36821     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36822     
36823     // private
36824     hiddenField: false,
36825     
36826     onRender : function(ct, position)
36827     {
36828         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36829         if (this.useIso) {
36830             this.el.dom.removeAttribute('name'); 
36831             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36832                     'before', true);
36833             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36834             // prevent input submission
36835             this.hiddenName = this.name;
36836         }
36837             
36838             
36839     },
36840     
36841     // private
36842     validateValue : function(value)
36843     {
36844         value = this.formatDate(value);
36845         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36846             return false;
36847         }
36848         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36849              return true;
36850         }
36851         var svalue = value;
36852         value = this.parseDate(value);
36853         if(!value){
36854             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36855             return false;
36856         }
36857         var time = value.getTime();
36858         if(this.minValue && time < this.minValue.getTime()){
36859             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36860             return false;
36861         }
36862         if(this.maxValue && time > this.maxValue.getTime()){
36863             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36864             return false;
36865         }
36866         if(this.disabledDays){
36867             var day = value.getDay();
36868             for(var i = 0; i < this.disabledDays.length; i++) {
36869                 if(day === this.disabledDays[i]){
36870                     this.markInvalid(this.disabledDaysText);
36871                     return false;
36872                 }
36873             }
36874         }
36875         var fvalue = this.formatDate(value);
36876         if(this.ddMatch && this.ddMatch.test(fvalue)){
36877             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36878             return false;
36879         }
36880         return true;
36881     },
36882
36883     // private
36884     // Provides logic to override the default TriggerField.validateBlur which just returns true
36885     validateBlur : function(){
36886         return !this.menu || !this.menu.isVisible();
36887     },
36888
36889     /**
36890      * Returns the current date value of the date field.
36891      * @return {Date} The date value
36892      */
36893     getValue : function(){
36894         
36895         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36896     },
36897
36898     /**
36899      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36900      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36901      * (the default format used is "m/d/y").
36902      * <br />Usage:
36903      * <pre><code>
36904 //All of these calls set the same date value (May 4, 2006)
36905
36906 //Pass a date object:
36907 var dt = new Date('5/4/06');
36908 dateField.setValue(dt);
36909
36910 //Pass a date string (default format):
36911 dateField.setValue('5/4/06');
36912
36913 //Pass a date string (custom format):
36914 dateField.format = 'Y-m-d';
36915 dateField.setValue('2006-5-4');
36916 </code></pre>
36917      * @param {String/Date} date The date or valid date string
36918      */
36919     setValue : function(date){
36920         if (this.hiddenField) {
36921             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36922         }
36923         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36924     },
36925
36926     // private
36927     parseDate : function(value){
36928         if(!value || value instanceof Date){
36929             return value;
36930         }
36931         var v = Date.parseDate(value, this.format);
36932         if(!v && this.altFormats){
36933             if(!this.altFormatsArray){
36934                 this.altFormatsArray = this.altFormats.split("|");
36935             }
36936             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36937                 v = Date.parseDate(value, this.altFormatsArray[i]);
36938             }
36939         }
36940         return v;
36941     },
36942
36943     // private
36944     formatDate : function(date, fmt){
36945         return (!date || !(date instanceof Date)) ?
36946                date : date.dateFormat(fmt || this.format);
36947     },
36948
36949     // private
36950     menuListeners : {
36951         select: function(m, d){
36952             this.setValue(d);
36953             this.fireEvent('select', this, d);
36954         },
36955         show : function(){ // retain focus styling
36956             this.onFocus();
36957         },
36958         hide : function(){
36959             this.focus.defer(10, this);
36960             var ml = this.menuListeners;
36961             this.menu.un("select", ml.select,  this);
36962             this.menu.un("show", ml.show,  this);
36963             this.menu.un("hide", ml.hide,  this);
36964         }
36965     },
36966
36967     // private
36968     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36969     onTriggerClick : function(){
36970         if(this.disabled){
36971             return;
36972         }
36973         if(this.menu == null){
36974             this.menu = new Roo.menu.DateMenu();
36975         }
36976         Roo.apply(this.menu.picker,  {
36977             showClear: this.allowBlank,
36978             minDate : this.minValue,
36979             maxDate : this.maxValue,
36980             disabledDatesRE : this.ddMatch,
36981             disabledDatesText : this.disabledDatesText,
36982             disabledDays : this.disabledDays,
36983             disabledDaysText : this.disabledDaysText,
36984             format : this.format,
36985             minText : String.format(this.minText, this.formatDate(this.minValue)),
36986             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36987         });
36988         this.menu.on(Roo.apply({}, this.menuListeners, {
36989             scope:this
36990         }));
36991         this.menu.picker.setValue(this.getValue() || new Date());
36992         this.menu.show(this.el, "tl-bl?");
36993     },
36994
36995     beforeBlur : function(){
36996         var v = this.parseDate(this.getRawValue());
36997         if(v){
36998             this.setValue(v);
36999         }
37000     }
37001
37002     /** @cfg {Boolean} grow @hide */
37003     /** @cfg {Number} growMin @hide */
37004     /** @cfg {Number} growMax @hide */
37005     /**
37006      * @hide
37007      * @method autoSize
37008      */
37009 });/*
37010  * Based on:
37011  * Ext JS Library 1.1.1
37012  * Copyright(c) 2006-2007, Ext JS, LLC.
37013  *
37014  * Originally Released Under LGPL - original licence link has changed is not relivant.
37015  *
37016  * Fork - LGPL
37017  * <script type="text/javascript">
37018  */
37019  
37020
37021 /**
37022  * @class Roo.form.ComboBox
37023  * @extends Roo.form.TriggerField
37024  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37025  * @constructor
37026  * Create a new ComboBox.
37027  * @param {Object} config Configuration options
37028  */
37029 Roo.form.ComboBox = function(config){
37030     Roo.form.ComboBox.superclass.constructor.call(this, config);
37031     this.addEvents({
37032         /**
37033          * @event expand
37034          * Fires when the dropdown list is expanded
37035              * @param {Roo.form.ComboBox} combo This combo box
37036              */
37037         'expand' : true,
37038         /**
37039          * @event collapse
37040          * Fires when the dropdown list is collapsed
37041              * @param {Roo.form.ComboBox} combo This combo box
37042              */
37043         'collapse' : true,
37044         /**
37045          * @event beforeselect
37046          * Fires before a list item is selected. Return false to cancel the selection.
37047              * @param {Roo.form.ComboBox} combo This combo box
37048              * @param {Roo.data.Record} record The data record returned from the underlying store
37049              * @param {Number} index The index of the selected item in the dropdown list
37050              */
37051         'beforeselect' : true,
37052         /**
37053          * @event select
37054          * Fires when a list item is selected
37055              * @param {Roo.form.ComboBox} combo This combo box
37056              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37057              * @param {Number} index The index of the selected item in the dropdown list
37058              */
37059         'select' : true,
37060         /**
37061          * @event beforequery
37062          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37063          * The event object passed has these properties:
37064              * @param {Roo.form.ComboBox} combo This combo box
37065              * @param {String} query The query
37066              * @param {Boolean} forceAll true to force "all" query
37067              * @param {Boolean} cancel true to cancel the query
37068              * @param {Object} e The query event object
37069              */
37070         'beforequery': true,
37071          /**
37072          * @event add
37073          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37074              * @param {Roo.form.ComboBox} combo This combo box
37075              */
37076         'add' : true,
37077         /**
37078          * @event edit
37079          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37080              * @param {Roo.form.ComboBox} combo This combo box
37081              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37082              */
37083         'edit' : true
37084         
37085         
37086     });
37087     if(this.transform){
37088         this.allowDomMove = false;
37089         var s = Roo.getDom(this.transform);
37090         if(!this.hiddenName){
37091             this.hiddenName = s.name;
37092         }
37093         if(!this.store){
37094             this.mode = 'local';
37095             var d = [], opts = s.options;
37096             for(var i = 0, len = opts.length;i < len; i++){
37097                 var o = opts[i];
37098                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37099                 if(o.selected) {
37100                     this.value = value;
37101                 }
37102                 d.push([value, o.text]);
37103             }
37104             this.store = new Roo.data.SimpleStore({
37105                 'id': 0,
37106                 fields: ['value', 'text'],
37107                 data : d
37108             });
37109             this.valueField = 'value';
37110             this.displayField = 'text';
37111         }
37112         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37113         if(!this.lazyRender){
37114             this.target = true;
37115             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37116             s.parentNode.removeChild(s); // remove it
37117             this.render(this.el.parentNode);
37118         }else{
37119             s.parentNode.removeChild(s); // remove it
37120         }
37121
37122     }
37123     if (this.store) {
37124         this.store = Roo.factory(this.store, Roo.data);
37125     }
37126     
37127     this.selectedIndex = -1;
37128     if(this.mode == 'local'){
37129         if(config.queryDelay === undefined){
37130             this.queryDelay = 10;
37131         }
37132         if(config.minChars === undefined){
37133             this.minChars = 0;
37134         }
37135     }
37136 };
37137
37138 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37139     /**
37140      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37141      */
37142     /**
37143      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37144      * rendering into an Roo.Editor, defaults to false)
37145      */
37146     /**
37147      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37148      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37149      */
37150     /**
37151      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37152      */
37153     /**
37154      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37155      * the dropdown list (defaults to undefined, with no header element)
37156      */
37157
37158      /**
37159      * @cfg {String/Roo.Template} tpl The template to use to render the output
37160      */
37161      
37162     // private
37163     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37164     /**
37165      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37166      */
37167     listWidth: undefined,
37168     /**
37169      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37170      * mode = 'remote' or 'text' if mode = 'local')
37171      */
37172     displayField: undefined,
37173     /**
37174      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37175      * mode = 'remote' or 'value' if mode = 'local'). 
37176      * Note: use of a valueField requires the user make a selection
37177      * in order for a value to be mapped.
37178      */
37179     valueField: undefined,
37180     /**
37181      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37182      * field's data value (defaults to the underlying DOM element's name)
37183      */
37184     hiddenName: undefined,
37185     /**
37186      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37187      */
37188     listClass: '',
37189     /**
37190      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37191      */
37192     selectedClass: 'x-combo-selected',
37193     /**
37194      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37195      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37196      * which displays a downward arrow icon).
37197      */
37198     triggerClass : 'x-form-arrow-trigger',
37199     /**
37200      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37201      */
37202     shadow:'sides',
37203     /**
37204      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37205      * anchor positions (defaults to 'tl-bl')
37206      */
37207     listAlign: 'tl-bl?',
37208     /**
37209      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37210      */
37211     maxHeight: 300,
37212     /**
37213      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37214      * query specified by the allQuery config option (defaults to 'query')
37215      */
37216     triggerAction: 'query',
37217     /**
37218      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37219      * (defaults to 4, does not apply if editable = false)
37220      */
37221     minChars : 4,
37222     /**
37223      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37224      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37225      */
37226     typeAhead: false,
37227     /**
37228      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37229      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37230      */
37231     queryDelay: 500,
37232     /**
37233      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37234      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37235      */
37236     pageSize: 0,
37237     /**
37238      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37239      * when editable = true (defaults to false)
37240      */
37241     selectOnFocus:false,
37242     /**
37243      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37244      */
37245     queryParam: 'query',
37246     /**
37247      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37248      * when mode = 'remote' (defaults to 'Loading...')
37249      */
37250     loadingText: 'Loading...',
37251     /**
37252      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37253      */
37254     resizable: false,
37255     /**
37256      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37257      */
37258     handleHeight : 8,
37259     /**
37260      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37261      * traditional select (defaults to true)
37262      */
37263     editable: true,
37264     /**
37265      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37266      */
37267     allQuery: '',
37268     /**
37269      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37270      */
37271     mode: 'remote',
37272     /**
37273      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37274      * listWidth has a higher value)
37275      */
37276     minListWidth : 70,
37277     /**
37278      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37279      * allow the user to set arbitrary text into the field (defaults to false)
37280      */
37281     forceSelection:false,
37282     /**
37283      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37284      * if typeAhead = true (defaults to 250)
37285      */
37286     typeAheadDelay : 250,
37287     /**
37288      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37289      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37290      */
37291     valueNotFoundText : undefined,
37292     /**
37293      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37294      */
37295     blockFocus : false,
37296     
37297     /**
37298      * @cfg {Boolean} disableClear Disable showing of clear button.
37299      */
37300     disableClear : false,
37301     /**
37302      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37303      */
37304     alwaysQuery : false,
37305     
37306     //private
37307     addicon : false,
37308     editicon: false,
37309     
37310     
37311     // private
37312     onRender : function(ct, position){
37313         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37314         if(this.hiddenName){
37315             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37316                     'before', true);
37317             this.hiddenField.value =
37318                 this.hiddenValue !== undefined ? this.hiddenValue :
37319                 this.value !== undefined ? this.value : '';
37320
37321             // prevent input submission
37322             this.el.dom.removeAttribute('name');
37323         }
37324         if(Roo.isGecko){
37325             this.el.dom.setAttribute('autocomplete', 'off');
37326         }
37327
37328         var cls = 'x-combo-list';
37329
37330         this.list = new Roo.Layer({
37331             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37332         });
37333
37334         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37335         this.list.setWidth(lw);
37336         this.list.swallowEvent('mousewheel');
37337         this.assetHeight = 0;
37338
37339         if(this.title){
37340             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37341             this.assetHeight += this.header.getHeight();
37342         }
37343
37344         this.innerList = this.list.createChild({cls:cls+'-inner'});
37345         this.innerList.on('mouseover', this.onViewOver, this);
37346         this.innerList.on('mousemove', this.onViewMove, this);
37347         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37348         
37349         if(this.allowBlank && !this.pageSize && !this.disableClear){
37350             this.footer = this.list.createChild({cls:cls+'-ft'});
37351             this.pageTb = new Roo.Toolbar(this.footer);
37352            
37353         }
37354         if(this.pageSize){
37355             this.footer = this.list.createChild({cls:cls+'-ft'});
37356             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37357                     {pageSize: this.pageSize});
37358             
37359         }
37360         
37361         if (this.pageTb && this.allowBlank && !this.disableClear) {
37362             var _this = this;
37363             this.pageTb.add(new Roo.Toolbar.Fill(), {
37364                 cls: 'x-btn-icon x-btn-clear',
37365                 text: '&#160;',
37366                 handler: function()
37367                 {
37368                     _this.collapse();
37369                     _this.clearValue();
37370                     _this.onSelect(false, -1);
37371                 }
37372             });
37373         }
37374         if (this.footer) {
37375             this.assetHeight += this.footer.getHeight();
37376         }
37377         
37378
37379         if(!this.tpl){
37380             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37381         }
37382
37383         this.view = new Roo.View(this.innerList, this.tpl, {
37384             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37385         });
37386
37387         this.view.on('click', this.onViewClick, this);
37388
37389         this.store.on('beforeload', this.onBeforeLoad, this);
37390         this.store.on('load', this.onLoad, this);
37391         this.store.on('loadexception', this.collapse, this);
37392
37393         if(this.resizable){
37394             this.resizer = new Roo.Resizable(this.list,  {
37395                pinned:true, handles:'se'
37396             });
37397             this.resizer.on('resize', function(r, w, h){
37398                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37399                 this.listWidth = w;
37400                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37401                 this.restrictHeight();
37402             }, this);
37403             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37404         }
37405         if(!this.editable){
37406             this.editable = true;
37407             this.setEditable(false);
37408         }  
37409         
37410         
37411         if (typeof(this.events.add.listeners) != 'undefined') {
37412             
37413             this.addicon = this.wrap.createChild(
37414                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37415        
37416             this.addicon.on('click', function(e) {
37417                 this.fireEvent('add', this);
37418             }, this);
37419         }
37420         if (typeof(this.events.edit.listeners) != 'undefined') {
37421             
37422             this.editicon = this.wrap.createChild(
37423                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37424             if (this.addicon) {
37425                 this.editicon.setStyle('margin-left', '40px');
37426             }
37427             this.editicon.on('click', function(e) {
37428                 
37429                 // we fire even  if inothing is selected..
37430                 this.fireEvent('edit', this, this.lastData );
37431                 
37432             }, this);
37433         }
37434         
37435         
37436         
37437     },
37438
37439     // private
37440     initEvents : function(){
37441         Roo.form.ComboBox.superclass.initEvents.call(this);
37442
37443         this.keyNav = new Roo.KeyNav(this.el, {
37444             "up" : function(e){
37445                 this.inKeyMode = true;
37446                 this.selectPrev();
37447             },
37448
37449             "down" : function(e){
37450                 if(!this.isExpanded()){
37451                     this.onTriggerClick();
37452                 }else{
37453                     this.inKeyMode = true;
37454                     this.selectNext();
37455                 }
37456             },
37457
37458             "enter" : function(e){
37459                 this.onViewClick();
37460                 //return true;
37461             },
37462
37463             "esc" : function(e){
37464                 this.collapse();
37465             },
37466
37467             "tab" : function(e){
37468                 this.onViewClick(false);
37469                 return true;
37470             },
37471
37472             scope : this,
37473
37474             doRelay : function(foo, bar, hname){
37475                 if(hname == 'down' || this.scope.isExpanded()){
37476                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37477                 }
37478                 return true;
37479             },
37480
37481             forceKeyDown: true
37482         });
37483         this.queryDelay = Math.max(this.queryDelay || 10,
37484                 this.mode == 'local' ? 10 : 250);
37485         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37486         if(this.typeAhead){
37487             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37488         }
37489         if(this.editable !== false){
37490             this.el.on("keyup", this.onKeyUp, this);
37491         }
37492         if(this.forceSelection){
37493             this.on('blur', this.doForce, this);
37494         }
37495     },
37496
37497     onDestroy : function(){
37498         if(this.view){
37499             this.view.setStore(null);
37500             this.view.el.removeAllListeners();
37501             this.view.el.remove();
37502             this.view.purgeListeners();
37503         }
37504         if(this.list){
37505             this.list.destroy();
37506         }
37507         if(this.store){
37508             this.store.un('beforeload', this.onBeforeLoad, this);
37509             this.store.un('load', this.onLoad, this);
37510             this.store.un('loadexception', this.collapse, this);
37511         }
37512         Roo.form.ComboBox.superclass.onDestroy.call(this);
37513     },
37514
37515     // private
37516     fireKey : function(e){
37517         if(e.isNavKeyPress() && !this.list.isVisible()){
37518             this.fireEvent("specialkey", this, e);
37519         }
37520     },
37521
37522     // private
37523     onResize: function(w, h){
37524         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37525         
37526         if(typeof w != 'number'){
37527             // we do not handle it!?!?
37528             return;
37529         }
37530         var tw = this.trigger.getWidth();
37531         tw += this.addicon ? this.addicon.getWidth() : 0;
37532         tw += this.editicon ? this.editicon.getWidth() : 0;
37533         var x = w - tw;
37534         this.el.setWidth( this.adjustWidth('input', x));
37535             
37536         this.trigger.setStyle('left', x+'px');
37537         
37538         if(this.list && this.listWidth === undefined){
37539             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37540             this.list.setWidth(lw);
37541             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37542         }
37543         
37544     
37545         
37546     },
37547
37548     /**
37549      * Allow or prevent the user from directly editing the field text.  If false is passed,
37550      * the user will only be able to select from the items defined in the dropdown list.  This method
37551      * is the runtime equivalent of setting the 'editable' config option at config time.
37552      * @param {Boolean} value True to allow the user to directly edit the field text
37553      */
37554     setEditable : function(value){
37555         if(value == this.editable){
37556             return;
37557         }
37558         this.editable = value;
37559         if(!value){
37560             this.el.dom.setAttribute('readOnly', true);
37561             this.el.on('mousedown', this.onTriggerClick,  this);
37562             this.el.addClass('x-combo-noedit');
37563         }else{
37564             this.el.dom.setAttribute('readOnly', false);
37565             this.el.un('mousedown', this.onTriggerClick,  this);
37566             this.el.removeClass('x-combo-noedit');
37567         }
37568     },
37569
37570     // private
37571     onBeforeLoad : function(){
37572         if(!this.hasFocus){
37573             return;
37574         }
37575         this.innerList.update(this.loadingText ?
37576                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37577         this.restrictHeight();
37578         this.selectedIndex = -1;
37579     },
37580
37581     // private
37582     onLoad : function(){
37583         if(!this.hasFocus){
37584             return;
37585         }
37586         if(this.store.getCount() > 0){
37587             this.expand();
37588             this.restrictHeight();
37589             if(this.lastQuery == this.allQuery){
37590                 if(this.editable){
37591                     this.el.dom.select();
37592                 }
37593                 if(!this.selectByValue(this.value, true)){
37594                     this.select(0, true);
37595                 }
37596             }else{
37597                 this.selectNext();
37598                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37599                     this.taTask.delay(this.typeAheadDelay);
37600                 }
37601             }
37602         }else{
37603             this.onEmptyResults();
37604         }
37605         //this.el.focus();
37606     },
37607
37608     // private
37609     onTypeAhead : function(){
37610         if(this.store.getCount() > 0){
37611             var r = this.store.getAt(0);
37612             var newValue = r.data[this.displayField];
37613             var len = newValue.length;
37614             var selStart = this.getRawValue().length;
37615             if(selStart != len){
37616                 this.setRawValue(newValue);
37617                 this.selectText(selStart, newValue.length);
37618             }
37619         }
37620     },
37621
37622     // private
37623     onSelect : function(record, index){
37624         if(this.fireEvent('beforeselect', this, record, index) !== false){
37625             this.setFromData(index > -1 ? record.data : false);
37626             this.collapse();
37627             this.fireEvent('select', this, record, index);
37628         }
37629     },
37630
37631     /**
37632      * Returns the currently selected field value or empty string if no value is set.
37633      * @return {String} value The selected value
37634      */
37635     getValue : function(){
37636         if(this.valueField){
37637             return typeof this.value != 'undefined' ? this.value : '';
37638         }else{
37639             return Roo.form.ComboBox.superclass.getValue.call(this);
37640         }
37641     },
37642
37643     /**
37644      * Clears any text/value currently set in the field
37645      */
37646     clearValue : function(){
37647         if(this.hiddenField){
37648             this.hiddenField.value = '';
37649         }
37650         this.value = '';
37651         this.setRawValue('');
37652         this.lastSelectionText = '';
37653         this.applyEmptyText();
37654     },
37655
37656     /**
37657      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37658      * will be displayed in the field.  If the value does not match the data value of an existing item,
37659      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37660      * Otherwise the field will be blank (although the value will still be set).
37661      * @param {String} value The value to match
37662      */
37663     setValue : function(v){
37664         var text = v;
37665         if(this.valueField){
37666             var r = this.findRecord(this.valueField, v);
37667             if(r){
37668                 text = r.data[this.displayField];
37669             }else if(this.valueNotFoundText !== undefined){
37670                 text = this.valueNotFoundText;
37671             }
37672         }
37673         this.lastSelectionText = text;
37674         if(this.hiddenField){
37675             this.hiddenField.value = v;
37676         }
37677         Roo.form.ComboBox.superclass.setValue.call(this, text);
37678         this.value = v;
37679     },
37680     /**
37681      * @property {Object} the last set data for the element
37682      */
37683     
37684     lastData : false,
37685     /**
37686      * Sets the value of the field based on a object which is related to the record format for the store.
37687      * @param {Object} value the value to set as. or false on reset?
37688      */
37689     setFromData : function(o){
37690         var dv = ''; // display value
37691         var vv = ''; // value value..
37692         this.lastData = o;
37693         if (this.displayField) {
37694             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37695         } else {
37696             // this is an error condition!!!
37697             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37698         }
37699         
37700         if(this.valueField){
37701             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37702         }
37703         if(this.hiddenField){
37704             this.hiddenField.value = vv;
37705             
37706             this.lastSelectionText = dv;
37707             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37708             this.value = vv;
37709             return;
37710         }
37711         // no hidden field.. - we store the value in 'value', but still display
37712         // display field!!!!
37713         this.lastSelectionText = dv;
37714         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37715         this.value = vv;
37716         
37717         
37718     },
37719     // private
37720     reset : function(){
37721         // overridden so that last data is reset..
37722         this.setValue(this.originalValue);
37723         this.clearInvalid();
37724         this.lastData = false;
37725     },
37726     // private
37727     findRecord : function(prop, value){
37728         var record;
37729         if(this.store.getCount() > 0){
37730             this.store.each(function(r){
37731                 if(r.data[prop] == value){
37732                     record = r;
37733                     return false;
37734                 }
37735             });
37736         }
37737         return record;
37738     },
37739
37740     // private
37741     onViewMove : function(e, t){
37742         this.inKeyMode = false;
37743     },
37744
37745     // private
37746     onViewOver : function(e, t){
37747         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37748             return;
37749         }
37750         var item = this.view.findItemFromChild(t);
37751         if(item){
37752             var index = this.view.indexOf(item);
37753             this.select(index, false);
37754         }
37755     },
37756
37757     // private
37758     onViewClick : function(doFocus){
37759         var index = this.view.getSelectedIndexes()[0];
37760         var r = this.store.getAt(index);
37761         if(r){
37762             this.onSelect(r, index);
37763         }
37764         if(doFocus !== false && !this.blockFocus){
37765             this.el.focus();
37766         }
37767     },
37768
37769     // private
37770     restrictHeight : function(){
37771         this.innerList.dom.style.height = '';
37772         var inner = this.innerList.dom;
37773         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37774         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37775         this.list.beginUpdate();
37776         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37777         this.list.alignTo(this.el, this.listAlign);
37778         this.list.endUpdate();
37779     },
37780
37781     // private
37782     onEmptyResults : function(){
37783         this.collapse();
37784     },
37785
37786     /**
37787      * Returns true if the dropdown list is expanded, else false.
37788      */
37789     isExpanded : function(){
37790         return this.list.isVisible();
37791     },
37792
37793     /**
37794      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37795      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37796      * @param {String} value The data value of the item to select
37797      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37798      * selected item if it is not currently in view (defaults to true)
37799      * @return {Boolean} True if the value matched an item in the list, else false
37800      */
37801     selectByValue : function(v, scrollIntoView){
37802         if(v !== undefined && v !== null){
37803             var r = this.findRecord(this.valueField || this.displayField, v);
37804             if(r){
37805                 this.select(this.store.indexOf(r), scrollIntoView);
37806                 return true;
37807             }
37808         }
37809         return false;
37810     },
37811
37812     /**
37813      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37814      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37815      * @param {Number} index The zero-based index of the list item to select
37816      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37817      * selected item if it is not currently in view (defaults to true)
37818      */
37819     select : function(index, scrollIntoView){
37820         this.selectedIndex = index;
37821         this.view.select(index);
37822         if(scrollIntoView !== false){
37823             var el = this.view.getNode(index);
37824             if(el){
37825                 this.innerList.scrollChildIntoView(el, false);
37826             }
37827         }
37828     },
37829
37830     // private
37831     selectNext : function(){
37832         var ct = this.store.getCount();
37833         if(ct > 0){
37834             if(this.selectedIndex == -1){
37835                 this.select(0);
37836             }else if(this.selectedIndex < ct-1){
37837                 this.select(this.selectedIndex+1);
37838             }
37839         }
37840     },
37841
37842     // private
37843     selectPrev : function(){
37844         var ct = this.store.getCount();
37845         if(ct > 0){
37846             if(this.selectedIndex == -1){
37847                 this.select(0);
37848             }else if(this.selectedIndex != 0){
37849                 this.select(this.selectedIndex-1);
37850             }
37851         }
37852     },
37853
37854     // private
37855     onKeyUp : function(e){
37856         if(this.editable !== false && !e.isSpecialKey()){
37857             this.lastKey = e.getKey();
37858             this.dqTask.delay(this.queryDelay);
37859         }
37860     },
37861
37862     // private
37863     validateBlur : function(){
37864         return !this.list || !this.list.isVisible();   
37865     },
37866
37867     // private
37868     initQuery : function(){
37869         this.doQuery(this.getRawValue());
37870     },
37871
37872     // private
37873     doForce : function(){
37874         if(this.el.dom.value.length > 0){
37875             this.el.dom.value =
37876                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37877             this.applyEmptyText();
37878         }
37879     },
37880
37881     /**
37882      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37883      * query allowing the query action to be canceled if needed.
37884      * @param {String} query The SQL query to execute
37885      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37886      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37887      * saved in the current store (defaults to false)
37888      */
37889     doQuery : function(q, forceAll){
37890         if(q === undefined || q === null){
37891             q = '';
37892         }
37893         var qe = {
37894             query: q,
37895             forceAll: forceAll,
37896             combo: this,
37897             cancel:false
37898         };
37899         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37900             return false;
37901         }
37902         q = qe.query;
37903         forceAll = qe.forceAll;
37904         if(forceAll === true || (q.length >= this.minChars)){
37905             if(this.lastQuery != q || this.alwaysQuery){
37906                 this.lastQuery = q;
37907                 if(this.mode == 'local'){
37908                     this.selectedIndex = -1;
37909                     if(forceAll){
37910                         this.store.clearFilter();
37911                     }else{
37912                         this.store.filter(this.displayField, q);
37913                     }
37914                     this.onLoad();
37915                 }else{
37916                     this.store.baseParams[this.queryParam] = q;
37917                     this.store.load({
37918                         params: this.getParams(q)
37919                     });
37920                     this.expand();
37921                 }
37922             }else{
37923                 this.selectedIndex = -1;
37924                 this.onLoad();   
37925             }
37926         }
37927     },
37928
37929     // private
37930     getParams : function(q){
37931         var p = {};
37932         //p[this.queryParam] = q;
37933         if(this.pageSize){
37934             p.start = 0;
37935             p.limit = this.pageSize;
37936         }
37937         return p;
37938     },
37939
37940     /**
37941      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37942      */
37943     collapse : function(){
37944         if(!this.isExpanded()){
37945             return;
37946         }
37947         this.list.hide();
37948         Roo.get(document).un('mousedown', this.collapseIf, this);
37949         Roo.get(document).un('mousewheel', this.collapseIf, this);
37950         if (!this.editable) {
37951             Roo.get(document).un('keydown', this.listKeyPress, this);
37952         }
37953         this.fireEvent('collapse', this);
37954     },
37955
37956     // private
37957     collapseIf : function(e){
37958         if(!e.within(this.wrap) && !e.within(this.list)){
37959             this.collapse();
37960         }
37961     },
37962
37963     /**
37964      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37965      */
37966     expand : function(){
37967         if(this.isExpanded() || !this.hasFocus){
37968             return;
37969         }
37970         this.list.alignTo(this.el, this.listAlign);
37971         this.list.show();
37972         Roo.get(document).on('mousedown', this.collapseIf, this);
37973         Roo.get(document).on('mousewheel', this.collapseIf, this);
37974         if (!this.editable) {
37975             Roo.get(document).on('keydown', this.listKeyPress, this);
37976         }
37977         
37978         this.fireEvent('expand', this);
37979     },
37980
37981     // private
37982     // Implements the default empty TriggerField.onTriggerClick function
37983     onTriggerClick : function(){
37984         if(this.disabled){
37985             return;
37986         }
37987         if(this.isExpanded()){
37988             this.collapse();
37989             if (!this.blockFocus) {
37990                 this.el.focus();
37991             }
37992             
37993         }else {
37994             this.hasFocus = true;
37995             if(this.triggerAction == 'all') {
37996                 this.doQuery(this.allQuery, true);
37997             } else {
37998                 this.doQuery(this.getRawValue());
37999             }
38000             if (!this.blockFocus) {
38001                 this.el.focus();
38002             }
38003         }
38004     },
38005     listKeyPress : function(e)
38006     {
38007         //Roo.log('listkeypress');
38008         // scroll to first matching element based on key pres..
38009         if (e.isSpecialKey()) {
38010             return false;
38011         }
38012         var k = String.fromCharCode(e.getKey()).toUpperCase();
38013         //Roo.log(k);
38014         var match  = false;
38015         var csel = this.view.getSelectedNodes();
38016         var cselitem = false;
38017         if (csel.length) {
38018             var ix = this.view.indexOf(csel[0]);
38019             cselitem  = this.store.getAt(ix);
38020             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38021                 cselitem = false;
38022             }
38023             
38024         }
38025         
38026         this.store.each(function(v) { 
38027             if (cselitem) {
38028                 // start at existing selection.
38029                 if (cselitem.id == v.id) {
38030                     cselitem = false;
38031                 }
38032                 return;
38033             }
38034                 
38035             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38036                 match = this.store.indexOf(v);
38037                 return false;
38038             }
38039         }, this);
38040         
38041         if (match === false) {
38042             return true; // no more action?
38043         }
38044         // scroll to?
38045         this.view.select(match);
38046         var sn = Roo.get(this.view.getSelectedNodes()[0])
38047         sn.scrollIntoView(sn.dom.parentNode, false);
38048     }
38049
38050     /** 
38051     * @cfg {Boolean} grow 
38052     * @hide 
38053     */
38054     /** 
38055     * @cfg {Number} growMin 
38056     * @hide 
38057     */
38058     /** 
38059     * @cfg {Number} growMax 
38060     * @hide 
38061     */
38062     /**
38063      * @hide
38064      * @method autoSize
38065      */
38066 });/*
38067  * Based on:
38068  * Ext JS Library 1.1.1
38069  * Copyright(c) 2006-2007, Ext JS, LLC.
38070  *
38071  * Originally Released Under LGPL - original licence link has changed is not relivant.
38072  *
38073  * Fork - LGPL
38074  * <script type="text/javascript">
38075  */
38076 /**
38077  * @class Roo.form.Checkbox
38078  * @extends Roo.form.Field
38079  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38080  * @constructor
38081  * Creates a new Checkbox
38082  * @param {Object} config Configuration options
38083  */
38084 Roo.form.Checkbox = function(config){
38085     Roo.form.Checkbox.superclass.constructor.call(this, config);
38086     this.addEvents({
38087         /**
38088          * @event check
38089          * Fires when the checkbox is checked or unchecked.
38090              * @param {Roo.form.Checkbox} this This checkbox
38091              * @param {Boolean} checked The new checked value
38092              */
38093         check : true
38094     });
38095 };
38096
38097 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38098     /**
38099      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38100      */
38101     focusClass : undefined,
38102     /**
38103      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38104      */
38105     fieldClass: "x-form-field",
38106     /**
38107      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38108      */
38109     checked: false,
38110     /**
38111      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38112      * {tag: "input", type: "checkbox", autocomplete: "off"})
38113      */
38114     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38115     /**
38116      * @cfg {String} boxLabel The text that appears beside the checkbox
38117      */
38118     boxLabel : "",
38119     /**
38120      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38121      */  
38122     inputValue : '1',
38123     /**
38124      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38125      */
38126      valueOff: '0', // value when not checked..
38127
38128     actionMode : 'viewEl', 
38129     //
38130     // private
38131     itemCls : 'x-menu-check-item x-form-item',
38132     groupClass : 'x-menu-group-item',
38133     inputType : 'hidden',
38134     
38135     
38136     inSetChecked: false, // check that we are not calling self...
38137     
38138     inputElement: false, // real input element?
38139     basedOn: false, // ????
38140     
38141     isFormField: true, // not sure where this is needed!!!!
38142
38143     onResize : function(){
38144         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38145         if(!this.boxLabel){
38146             this.el.alignTo(this.wrap, 'c-c');
38147         }
38148     },
38149
38150     initEvents : function(){
38151         Roo.form.Checkbox.superclass.initEvents.call(this);
38152         this.el.on("click", this.onClick,  this);
38153         this.el.on("change", this.onClick,  this);
38154     },
38155
38156
38157     getResizeEl : function(){
38158         return this.wrap;
38159     },
38160
38161     getPositionEl : function(){
38162         return this.wrap;
38163     },
38164
38165     // private
38166     onRender : function(ct, position){
38167         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38168         /*
38169         if(this.inputValue !== undefined){
38170             this.el.dom.value = this.inputValue;
38171         }
38172         */
38173         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38174         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38175         var viewEl = this.wrap.createChild({ 
38176             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38177         this.viewEl = viewEl;   
38178         this.wrap.on('click', this.onClick,  this); 
38179         
38180         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38181         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38182         
38183         
38184         
38185         if(this.boxLabel){
38186             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38187         //    viewEl.on('click', this.onClick,  this); 
38188         }
38189         //if(this.checked){
38190             this.setChecked(this.checked);
38191         //}else{
38192             //this.checked = this.el.dom;
38193         //}
38194
38195     },
38196
38197     // private
38198     initValue : Roo.emptyFn,
38199
38200     /**
38201      * Returns the checked state of the checkbox.
38202      * @return {Boolean} True if checked, else false
38203      */
38204     getValue : function(){
38205         if(this.el){
38206             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38207         }
38208         return this.valueOff;
38209         
38210     },
38211
38212         // private
38213     onClick : function(){ 
38214         this.setChecked(!this.checked);
38215
38216         //if(this.el.dom.checked != this.checked){
38217         //    this.setValue(this.el.dom.checked);
38218        // }
38219     },
38220
38221     /**
38222      * Sets the checked state of the checkbox.
38223      * On is always based on a string comparison between inputValue and the param.
38224      * @param {Boolean/String} value - the value to set 
38225      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38226      */
38227     setValue : function(v,suppressEvent){
38228         
38229         
38230         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38231         //if(this.el && this.el.dom){
38232         //    this.el.dom.checked = this.checked;
38233         //    this.el.dom.defaultChecked = this.checked;
38234         //}
38235         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38236         //this.fireEvent("check", this, this.checked);
38237     },
38238     // private..
38239     setChecked : function(state,suppressEvent)
38240     {
38241         if (this.inSetChecked) {
38242             this.checked = state;
38243             return;
38244         }
38245         
38246     
38247         if(this.wrap){
38248             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38249         }
38250         this.checked = state;
38251         if(suppressEvent !== true){
38252             this.fireEvent('check', this, state);
38253         }
38254         this.inSetChecked = true;
38255         this.el.dom.value = state ? this.inputValue : this.valueOff;
38256         this.inSetChecked = false;
38257         
38258     },
38259     // handle setting of hidden value by some other method!!?!?
38260     setFromHidden: function()
38261     {
38262         if(!this.el){
38263             return;
38264         }
38265         //console.log("SET FROM HIDDEN");
38266         //alert('setFrom hidden');
38267         this.setValue(this.el.dom.value);
38268     },
38269     
38270     onDestroy : function()
38271     {
38272         if(this.viewEl){
38273             Roo.get(this.viewEl).remove();
38274         }
38275          
38276         Roo.form.Checkbox.superclass.onDestroy.call(this);
38277     }
38278
38279 });/*
38280  * Based on:
38281  * Ext JS Library 1.1.1
38282  * Copyright(c) 2006-2007, Ext JS, LLC.
38283  *
38284  * Originally Released Under LGPL - original licence link has changed is not relivant.
38285  *
38286  * Fork - LGPL
38287  * <script type="text/javascript">
38288  */
38289  
38290 /**
38291  * @class Roo.form.Radio
38292  * @extends Roo.form.Checkbox
38293  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38294  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38295  * @constructor
38296  * Creates a new Radio
38297  * @param {Object} config Configuration options
38298  */
38299 Roo.form.Radio = function(){
38300     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38301 };
38302 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38303     inputType: 'radio',
38304
38305     /**
38306      * If this radio is part of a group, it will return the selected value
38307      * @return {String}
38308      */
38309     getGroupValue : function(){
38310         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38311     }
38312 });//<script type="text/javascript">
38313
38314 /*
38315  * Ext JS Library 1.1.1
38316  * Copyright(c) 2006-2007, Ext JS, LLC.
38317  * licensing@extjs.com
38318  * 
38319  * http://www.extjs.com/license
38320  */
38321  
38322  /*
38323   * 
38324   * Known bugs:
38325   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38326   * - IE ? - no idea how much works there.
38327   * 
38328   * 
38329   * 
38330   */
38331  
38332
38333 /**
38334  * @class Ext.form.HtmlEditor
38335  * @extends Ext.form.Field
38336  * Provides a lightweight HTML Editor component.
38337  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38338  * 
38339  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38340  * supported by this editor.</b><br/><br/>
38341  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38342  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38343  */
38344 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38345       /**
38346      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38347      */
38348     toolbars : false,
38349     /**
38350      * @cfg {String} createLinkText The default text for the create link prompt
38351      */
38352     createLinkText : 'Please enter the URL for the link:',
38353     /**
38354      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38355      */
38356     defaultLinkValue : 'http:/'+'/',
38357    
38358     
38359     // id of frame..
38360     frameId: false,
38361     
38362     // private properties
38363     validationEvent : false,
38364     deferHeight: true,
38365     initialized : false,
38366     activated : false,
38367     sourceEditMode : false,
38368     onFocus : Roo.emptyFn,
38369     iframePad:3,
38370     hideMode:'offsets',
38371     defaultAutoCreate : {
38372         tag: "textarea",
38373         style:"width:500px;height:300px;",
38374         autocomplete: "off"
38375     },
38376
38377     // private
38378     initComponent : function(){
38379         this.addEvents({
38380             /**
38381              * @event initialize
38382              * Fires when the editor is fully initialized (including the iframe)
38383              * @param {HtmlEditor} this
38384              */
38385             initialize: true,
38386             /**
38387              * @event activate
38388              * Fires when the editor is first receives the focus. Any insertion must wait
38389              * until after this event.
38390              * @param {HtmlEditor} this
38391              */
38392             activate: true,
38393              /**
38394              * @event beforesync
38395              * Fires before the textarea is updated with content from the editor iframe. Return false
38396              * to cancel the sync.
38397              * @param {HtmlEditor} this
38398              * @param {String} html
38399              */
38400             beforesync: true,
38401              /**
38402              * @event beforepush
38403              * Fires before the iframe editor is updated with content from the textarea. Return false
38404              * to cancel the push.
38405              * @param {HtmlEditor} this
38406              * @param {String} html
38407              */
38408             beforepush: true,
38409              /**
38410              * @event sync
38411              * Fires when the textarea is updated with content from the editor iframe.
38412              * @param {HtmlEditor} this
38413              * @param {String} html
38414              */
38415             sync: true,
38416              /**
38417              * @event push
38418              * Fires when the iframe editor is updated with content from the textarea.
38419              * @param {HtmlEditor} this
38420              * @param {String} html
38421              */
38422             push: true,
38423              /**
38424              * @event editmodechange
38425              * Fires when the editor switches edit modes
38426              * @param {HtmlEditor} this
38427              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38428              */
38429             editmodechange: true,
38430             /**
38431              * @event editorevent
38432              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38433              * @param {HtmlEditor} this
38434              */
38435             editorevent: true
38436         })
38437     },
38438
38439     /**
38440      * Protected method that will not generally be called directly. It
38441      * is called when the editor creates its toolbar. Override this method if you need to
38442      * add custom toolbar buttons.
38443      * @param {HtmlEditor} editor
38444      */
38445     createToolbar : function(editor){
38446         if (!editor.toolbars || !editor.toolbars.length) {
38447             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38448         }
38449         
38450         for (var i =0 ; i < editor.toolbars.length;i++) {
38451             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38452             editor.toolbars[i].init(editor);
38453         }
38454          
38455         
38456     },
38457
38458     /**
38459      * Protected method that will not generally be called directly. It
38460      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38461      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38462      */
38463     getDocMarkup : function(){
38464         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38465     },
38466
38467     // private
38468     onRender : function(ct, position){
38469         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38470         this.el.dom.style.border = '0 none';
38471         this.el.dom.setAttribute('tabIndex', -1);
38472         this.el.addClass('x-hidden');
38473         if(Roo.isIE){ // fix IE 1px bogus margin
38474             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38475         }
38476         this.wrap = this.el.wrap({
38477             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38478         });
38479
38480         this.frameId = Roo.id();
38481         this.createToolbar(this);
38482         
38483         
38484         
38485         
38486       
38487         
38488         var iframe = this.wrap.createChild({
38489             tag: 'iframe',
38490             id: this.frameId,
38491             name: this.frameId,
38492             frameBorder : 'no',
38493             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38494         });
38495         
38496        // console.log(iframe);
38497         //this.wrap.dom.appendChild(iframe);
38498
38499         this.iframe = iframe.dom;
38500
38501          this.assignDocWin();
38502         
38503         this.doc.designMode = 'on';
38504        
38505         this.doc.open();
38506         this.doc.write(this.getDocMarkup());
38507         this.doc.close();
38508
38509         
38510         var task = { // must defer to wait for browser to be ready
38511             run : function(){
38512                 //console.log("run task?" + this.doc.readyState);
38513                 this.assignDocWin();
38514                 if(this.doc.body || this.doc.readyState == 'complete'){
38515                     try {
38516                         this.doc.designMode="on";
38517                     } catch (e) {
38518                         return;
38519                     }
38520                     Roo.TaskMgr.stop(task);
38521                     this.initEditor.defer(10, this);
38522                 }
38523             },
38524             interval : 10,
38525             duration:10000,
38526             scope: this
38527         };
38528         Roo.TaskMgr.start(task);
38529
38530         if(!this.width){
38531             this.setSize(this.el.getSize());
38532         }
38533     },
38534
38535     // private
38536     onResize : function(w, h){
38537         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38538         if(this.el && this.iframe){
38539             if(typeof w == 'number'){
38540                 var aw = w - this.wrap.getFrameWidth('lr');
38541                 this.el.setWidth(this.adjustWidth('textarea', aw));
38542                 this.iframe.style.width = aw + 'px';
38543             }
38544             if(typeof h == 'number'){
38545                 var tbh = 0;
38546                 for (var i =0; i < this.toolbars.length;i++) {
38547                     // fixme - ask toolbars for heights?
38548                     tbh += this.toolbars[i].tb.el.getHeight();
38549                 }
38550                 
38551                 
38552                 
38553                 
38554                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38555                 this.el.setHeight(this.adjustWidth('textarea', ah));
38556                 this.iframe.style.height = ah + 'px';
38557                 if(this.doc){
38558                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38559                 }
38560             }
38561         }
38562     },
38563
38564     /**
38565      * Toggles the editor between standard and source edit mode.
38566      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38567      */
38568     toggleSourceEdit : function(sourceEditMode){
38569         
38570         this.sourceEditMode = sourceEditMode === true;
38571         
38572         if(this.sourceEditMode){
38573           
38574             this.syncValue();
38575             this.iframe.className = 'x-hidden';
38576             this.el.removeClass('x-hidden');
38577             this.el.dom.removeAttribute('tabIndex');
38578             this.el.focus();
38579         }else{
38580              
38581             this.pushValue();
38582             this.iframe.className = '';
38583             this.el.addClass('x-hidden');
38584             this.el.dom.setAttribute('tabIndex', -1);
38585             this.deferFocus();
38586         }
38587         this.setSize(this.wrap.getSize());
38588         this.fireEvent('editmodechange', this, this.sourceEditMode);
38589     },
38590
38591     // private used internally
38592     createLink : function(){
38593         var url = prompt(this.createLinkText, this.defaultLinkValue);
38594         if(url && url != 'http:/'+'/'){
38595             this.relayCmd('createlink', url);
38596         }
38597     },
38598
38599     // private (for BoxComponent)
38600     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38601
38602     // private (for BoxComponent)
38603     getResizeEl : function(){
38604         return this.wrap;
38605     },
38606
38607     // private (for BoxComponent)
38608     getPositionEl : function(){
38609         return this.wrap;
38610     },
38611
38612     // private
38613     initEvents : function(){
38614         this.originalValue = this.getValue();
38615     },
38616
38617     /**
38618      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38619      * @method
38620      */
38621     markInvalid : Roo.emptyFn,
38622     /**
38623      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38624      * @method
38625      */
38626     clearInvalid : Roo.emptyFn,
38627
38628     setValue : function(v){
38629         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38630         this.pushValue();
38631     },
38632
38633     /**
38634      * Protected method that will not generally be called directly. If you need/want
38635      * custom HTML cleanup, this is the method you should override.
38636      * @param {String} html The HTML to be cleaned
38637      * return {String} The cleaned HTML
38638      */
38639     cleanHtml : function(html){
38640         html = String(html);
38641         if(html.length > 5){
38642             if(Roo.isSafari){ // strip safari nonsense
38643                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38644             }
38645         }
38646         if(html == '&nbsp;'){
38647             html = '';
38648         }
38649         return html;
38650     },
38651
38652     /**
38653      * Protected method that will not generally be called directly. Syncs the contents
38654      * of the editor iframe with the textarea.
38655      */
38656     syncValue : function(){
38657         if(this.initialized){
38658             var bd = (this.doc.body || this.doc.documentElement);
38659             var html = bd.innerHTML;
38660             if(Roo.isSafari){
38661                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38662                 var m = bs.match(/text-align:(.*?);/i);
38663                 if(m && m[1]){
38664                     html = '<div style="'+m[0]+'">' + html + '</div>';
38665                 }
38666             }
38667             html = this.cleanHtml(html);
38668             if(this.fireEvent('beforesync', this, html) !== false){
38669                 this.el.dom.value = html;
38670                 this.fireEvent('sync', this, html);
38671             }
38672         }
38673     },
38674
38675     /**
38676      * Protected method that will not generally be called directly. Pushes the value of the textarea
38677      * into the iframe editor.
38678      */
38679     pushValue : function(){
38680         if(this.initialized){
38681             var v = this.el.dom.value;
38682             if(v.length < 1){
38683                 v = '&#160;';
38684             }
38685             if(this.fireEvent('beforepush', this, v) !== false){
38686                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38687                 this.fireEvent('push', this, v);
38688             }
38689         }
38690     },
38691
38692     // private
38693     deferFocus : function(){
38694         this.focus.defer(10, this);
38695     },
38696
38697     // doc'ed in Field
38698     focus : function(){
38699         if(this.win && !this.sourceEditMode){
38700             this.win.focus();
38701         }else{
38702             this.el.focus();
38703         }
38704     },
38705     
38706     assignDocWin: function()
38707     {
38708         var iframe = this.iframe;
38709         
38710          if(Roo.isIE){
38711             this.doc = iframe.contentWindow.document;
38712             this.win = iframe.contentWindow;
38713         } else {
38714             if (!Roo.get(this.frameId)) {
38715                 return;
38716             }
38717             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38718             this.win = Roo.get(this.frameId).dom.contentWindow;
38719         }
38720     },
38721     
38722     // private
38723     initEditor : function(){
38724         //console.log("INIT EDITOR");
38725         this.assignDocWin();
38726         
38727         
38728         
38729         this.doc.designMode="on";
38730         this.doc.open();
38731         this.doc.write(this.getDocMarkup());
38732         this.doc.close();
38733         
38734         var dbody = (this.doc.body || this.doc.documentElement);
38735         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38736         // this copies styles from the containing element into thsi one..
38737         // not sure why we need all of this..
38738         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38739         ss['background-attachment'] = 'fixed'; // w3c
38740         dbody.bgProperties = 'fixed'; // ie
38741         Roo.DomHelper.applyStyles(dbody, ss);
38742         Roo.EventManager.on(this.doc, {
38743             'mousedown': this.onEditorEvent,
38744             'dblclick': this.onEditorEvent,
38745             'click': this.onEditorEvent,
38746             'keyup': this.onEditorEvent,
38747             buffer:100,
38748             scope: this
38749         });
38750         if(Roo.isGecko){
38751             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38752         }
38753         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38754             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38755         }
38756         this.initialized = true;
38757
38758         this.fireEvent('initialize', this);
38759         this.pushValue();
38760     },
38761
38762     // private
38763     onDestroy : function(){
38764         
38765         
38766         
38767         if(this.rendered){
38768             
38769             for (var i =0; i < this.toolbars.length;i++) {
38770                 // fixme - ask toolbars for heights?
38771                 this.toolbars[i].onDestroy();
38772             }
38773             
38774             this.wrap.dom.innerHTML = '';
38775             this.wrap.remove();
38776         }
38777     },
38778
38779     // private
38780     onFirstFocus : function(){
38781         
38782         this.assignDocWin();
38783         
38784         
38785         this.activated = true;
38786         for (var i =0; i < this.toolbars.length;i++) {
38787             this.toolbars[i].onFirstFocus();
38788         }
38789        
38790         if(Roo.isGecko){ // prevent silly gecko errors
38791             this.win.focus();
38792             var s = this.win.getSelection();
38793             if(!s.focusNode || s.focusNode.nodeType != 3){
38794                 var r = s.getRangeAt(0);
38795                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38796                 r.collapse(true);
38797                 this.deferFocus();
38798             }
38799             try{
38800                 this.execCmd('useCSS', true);
38801                 this.execCmd('styleWithCSS', false);
38802             }catch(e){}
38803         }
38804         this.fireEvent('activate', this);
38805     },
38806
38807     // private
38808     adjustFont: function(btn){
38809         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38810         //if(Roo.isSafari){ // safari
38811         //    adjust *= 2;
38812        // }
38813         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38814         if(Roo.isSafari){ // safari
38815             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38816             v =  (v < 10) ? 10 : v;
38817             v =  (v > 48) ? 48 : v;
38818             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38819             
38820         }
38821         
38822         
38823         v = Math.max(1, v+adjust);
38824         
38825         this.execCmd('FontSize', v  );
38826     },
38827
38828     onEditorEvent : function(e){
38829         this.fireEvent('editorevent', this, e);
38830       //  this.updateToolbar();
38831         this.syncValue();
38832     },
38833
38834     insertTag : function(tg)
38835     {
38836         // could be a bit smarter... -> wrap the current selected tRoo..
38837         
38838         this.execCmd("formatblock",   tg);
38839         
38840     },
38841     
38842     insertText : function(txt)
38843     {
38844         
38845         
38846         range = this.createRange();
38847         range.deleteContents();
38848                //alert(Sender.getAttribute('label'));
38849                
38850         range.insertNode(this.doc.createTextNode(txt));
38851     } ,
38852     
38853     // private
38854     relayBtnCmd : function(btn){
38855         this.relayCmd(btn.cmd);
38856     },
38857
38858     /**
38859      * Executes a Midas editor command on the editor document and performs necessary focus and
38860      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38861      * @param {String} cmd The Midas command
38862      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38863      */
38864     relayCmd : function(cmd, value){
38865         this.win.focus();
38866         this.execCmd(cmd, value);
38867         this.fireEvent('editorevent', this);
38868         //this.updateToolbar();
38869         this.deferFocus();
38870     },
38871
38872     /**
38873      * Executes a Midas editor command directly on the editor document.
38874      * For visual commands, you should use {@link #relayCmd} instead.
38875      * <b>This should only be called after the editor is initialized.</b>
38876      * @param {String} cmd The Midas command
38877      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38878      */
38879     execCmd : function(cmd, value){
38880         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38881         this.syncValue();
38882     },
38883
38884    
38885     /**
38886      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38887      * to insert tRoo.
38888      * @param {String} text
38889      */
38890     insertAtCursor : function(text){
38891         if(!this.activated){
38892             return;
38893         }
38894         if(Roo.isIE){
38895             this.win.focus();
38896             var r = this.doc.selection.createRange();
38897             if(r){
38898                 r.collapse(true);
38899                 r.pasteHTML(text);
38900                 this.syncValue();
38901                 this.deferFocus();
38902             }
38903         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38904             this.win.focus();
38905             this.execCmd('InsertHTML', text);
38906             this.deferFocus();
38907         }
38908     },
38909  // private
38910     mozKeyPress : function(e){
38911         if(e.ctrlKey){
38912             var c = e.getCharCode(), cmd;
38913           
38914             if(c > 0){
38915                 c = String.fromCharCode(c).toLowerCase();
38916                 switch(c){
38917                     case 'b':
38918                         cmd = 'bold';
38919                     break;
38920                     case 'i':
38921                         cmd = 'italic';
38922                     break;
38923                     case 'u':
38924                         cmd = 'underline';
38925                     case 'v':
38926                         this.cleanUpPaste.defer(100, this);
38927                         return;
38928                     break;
38929                 }
38930                 if(cmd){
38931                     this.win.focus();
38932                     this.execCmd(cmd);
38933                     this.deferFocus();
38934                     e.preventDefault();
38935                 }
38936                 
38937             }
38938         }
38939     },
38940
38941     // private
38942     fixKeys : function(){ // load time branching for fastest keydown performance
38943         if(Roo.isIE){
38944             return function(e){
38945                 var k = e.getKey(), r;
38946                 if(k == e.TAB){
38947                     e.stopEvent();
38948                     r = this.doc.selection.createRange();
38949                     if(r){
38950                         r.collapse(true);
38951                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38952                         this.deferFocus();
38953                     }
38954                     return;
38955                 }
38956                 
38957                 if(k == e.ENTER){
38958                     r = this.doc.selection.createRange();
38959                     if(r){
38960                         var target = r.parentElement();
38961                         if(!target || target.tagName.toLowerCase() != 'li'){
38962                             e.stopEvent();
38963                             r.pasteHTML('<br />');
38964                             r.collapse(false);
38965                             r.select();
38966                         }
38967                     }
38968                 }
38969                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38970                     this.cleanUpPaste.defer(100, this);
38971                     return;
38972                 }
38973                 
38974                 
38975             };
38976         }else if(Roo.isOpera){
38977             return function(e){
38978                 var k = e.getKey();
38979                 if(k == e.TAB){
38980                     e.stopEvent();
38981                     this.win.focus();
38982                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38983                     this.deferFocus();
38984                 }
38985                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38986                     this.cleanUpPaste.defer(100, this);
38987                     return;
38988                 }
38989                 
38990             };
38991         }else if(Roo.isSafari){
38992             return function(e){
38993                 var k = e.getKey();
38994                 
38995                 if(k == e.TAB){
38996                     e.stopEvent();
38997                     this.execCmd('InsertText','\t');
38998                     this.deferFocus();
38999                     return;
39000                 }
39001                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39002                     this.cleanUpPaste.defer(100, this);
39003                     return;
39004                 }
39005                 
39006              };
39007         }
39008     }(),
39009     
39010     getAllAncestors: function()
39011     {
39012         var p = this.getSelectedNode();
39013         var a = [];
39014         if (!p) {
39015             a.push(p); // push blank onto stack..
39016             p = this.getParentElement();
39017         }
39018         
39019         
39020         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39021             a.push(p);
39022             p = p.parentNode;
39023         }
39024         a.push(this.doc.body);
39025         return a;
39026     },
39027     lastSel : false,
39028     lastSelNode : false,
39029     
39030     
39031     getSelection : function() 
39032     {
39033         this.assignDocWin();
39034         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39035     },
39036     
39037     getSelectedNode: function() 
39038     {
39039         // this may only work on Gecko!!!
39040         
39041         // should we cache this!!!!
39042         
39043         
39044         
39045          
39046         var range = this.createRange(this.getSelection());
39047         
39048         if (Roo.isIE) {
39049             var parent = range.parentElement();
39050             while (true) {
39051                 var testRange = range.duplicate();
39052                 testRange.moveToElementText(parent);
39053                 if (testRange.inRange(range)) {
39054                     break;
39055                 }
39056                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39057                     break;
39058                 }
39059                 parent = parent.parentElement;
39060             }
39061             return parent;
39062         }
39063         
39064         
39065         var ar = range.endContainer.childNodes;
39066         if (!ar.length) {
39067             ar = range.commonAncestorContainer.childNodes;
39068             //alert(ar.length);
39069         }
39070         var nodes = [];
39071         var other_nodes = [];
39072         var has_other_nodes = false;
39073         for (var i=0;i<ar.length;i++) {
39074             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39075                 continue;
39076             }
39077             // fullly contained node.
39078             
39079             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39080                 nodes.push(ar[i]);
39081                 continue;
39082             }
39083             
39084             // probably selected..
39085             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39086                 other_nodes.push(ar[i]);
39087                 continue;
39088             }
39089             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39090                 continue;
39091             }
39092             
39093             
39094             has_other_nodes = true;
39095         }
39096         if (!nodes.length && other_nodes.length) {
39097             nodes= other_nodes;
39098         }
39099         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39100             return false;
39101         }
39102         
39103         return nodes[0];
39104     },
39105     createRange: function(sel)
39106     {
39107         // this has strange effects when using with 
39108         // top toolbar - not sure if it's a great idea.
39109         //this.editor.contentWindow.focus();
39110         if (typeof sel != "undefined") {
39111             try {
39112                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39113             } catch(e) {
39114                 return this.doc.createRange();
39115             }
39116         } else {
39117             return this.doc.createRange();
39118         }
39119     },
39120     getParentElement: function()
39121     {
39122         
39123         this.assignDocWin();
39124         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39125         
39126         var range = this.createRange(sel);
39127          
39128         try {
39129             var p = range.commonAncestorContainer;
39130             while (p.nodeType == 3) { // text node
39131                 p = p.parentNode;
39132             }
39133             return p;
39134         } catch (e) {
39135             return null;
39136         }
39137     
39138     },
39139     
39140     
39141     
39142     // BC Hacks - cause I cant work out what i was trying to do..
39143     rangeIntersectsNode : function(range, node)
39144     {
39145         var nodeRange = node.ownerDocument.createRange();
39146         try {
39147             nodeRange.selectNode(node);
39148         }
39149         catch (e) {
39150             nodeRange.selectNodeContents(node);
39151         }
39152
39153         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
39154                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
39155     },
39156     rangeCompareNode : function(range, node) {
39157         var nodeRange = node.ownerDocument.createRange();
39158         try {
39159             nodeRange.selectNode(node);
39160         } catch (e) {
39161             nodeRange.selectNodeContents(node);
39162         }
39163         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
39164         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
39165
39166         if (nodeIsBefore && !nodeIsAfter)
39167             return 0;
39168         if (!nodeIsBefore && nodeIsAfter)
39169             return 1;
39170         if (nodeIsBefore && nodeIsAfter)
39171             return 2;
39172
39173         return 3;
39174     },
39175
39176     // private? - in a new class?
39177     cleanUpPaste :  function()
39178     {
39179         // cleans up the whole document..
39180       //  console.log('cleanuppaste');
39181         this.cleanUpChildren(this.doc.body)
39182         
39183         
39184     },
39185     cleanUpChildren : function (n)
39186     {
39187         if (!n.childNodes.length) {
39188             return;
39189         }
39190         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39191            this.cleanUpChild(n.childNodes[i]);
39192         }
39193     },
39194     
39195     
39196         
39197     
39198     cleanUpChild : function (node)
39199     {
39200         //console.log(node);
39201         if (node.nodeName == "#text") {
39202             // clean up silly Windows -- stuff?
39203             return; 
39204         }
39205         if (node.nodeName == "#comment") {
39206             node.parentNode.removeChild(node);
39207             // clean up silly Windows -- stuff?
39208             return; 
39209         }
39210         
39211         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39212             // remove node.
39213             node.parentNode.removeChild(node);
39214             return;
39215             
39216         }
39217         if (!node.attributes || !node.attributes.length) {
39218             this.cleanUpChildren(node);
39219             return;
39220         }
39221         
39222         function cleanAttr(n,v)
39223         {
39224             
39225             if (v.match(/^\./) || v.match(/^\//)) {
39226                 return;
39227             }
39228             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39229                 return;
39230             }
39231             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39232             node.removeAttribute(n);
39233             
39234         }
39235         
39236         function cleanStyle(n,v)
39237         {
39238             if (v.match(/expression/)) { //XSS?? should we even bother..
39239                 node.removeAttribute(n);
39240                 return;
39241             }
39242             
39243             
39244             var parts = v.split(/;/);
39245             Roo.each(parts, function(p) {
39246                 p = p.replace(/\s+/g,'');
39247                 if (!p.length) {
39248                     return;
39249                 }
39250                 var l = p.split(':').shift().replace(/\s+/g,'');
39251                 
39252                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39253                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39254                     node.removeAttribute(n);
39255                     return false;
39256                 }
39257             });
39258             
39259             
39260         }
39261         
39262         
39263         for (var i = node.attributes.length-1; i > -1 ; i--) {
39264             var a = node.attributes[i];
39265             //console.log(a);
39266             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39267                 node.removeAttribute(a.name);
39268                 return;
39269             }
39270             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39271                 cleanAttr(a.name,a.value); // fixme..
39272                 return;
39273             }
39274             if (a.name == 'style') {
39275                 cleanStyle(a.name,a.value);
39276             }
39277             /// clean up MS crap..
39278             if (a.name == 'class') {
39279                 if (a.value.match(/^Mso/)) {
39280                     node.className = '';
39281                 }
39282             }
39283             
39284             // style cleanup!?
39285             // class cleanup?
39286             
39287         }
39288         
39289         
39290         this.cleanUpChildren(node);
39291         
39292         
39293     }
39294     
39295     
39296     // hide stuff that is not compatible
39297     /**
39298      * @event blur
39299      * @hide
39300      */
39301     /**
39302      * @event change
39303      * @hide
39304      */
39305     /**
39306      * @event focus
39307      * @hide
39308      */
39309     /**
39310      * @event specialkey
39311      * @hide
39312      */
39313     /**
39314      * @cfg {String} fieldClass @hide
39315      */
39316     /**
39317      * @cfg {String} focusClass @hide
39318      */
39319     /**
39320      * @cfg {String} autoCreate @hide
39321      */
39322     /**
39323      * @cfg {String} inputType @hide
39324      */
39325     /**
39326      * @cfg {String} invalidClass @hide
39327      */
39328     /**
39329      * @cfg {String} invalidText @hide
39330      */
39331     /**
39332      * @cfg {String} msgFx @hide
39333      */
39334     /**
39335      * @cfg {String} validateOnBlur @hide
39336      */
39337 });
39338
39339 Roo.form.HtmlEditor.white = [
39340         'area', 'br', 'img', 'input', 'hr', 'wbr',
39341         
39342        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39343        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39344        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39345        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39346        'table',   'ul',         'xmp', 
39347        
39348        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39349       'thead',   'tr', 
39350      
39351       'dir', 'menu', 'ol', 'ul', 'dl',
39352        
39353       'embed',  'object'
39354 ];
39355
39356
39357 Roo.form.HtmlEditor.black = [
39358     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39359         'applet', // 
39360         'base',   'basefont', 'bgsound', 'blink',  'body', 
39361         'frame',  'frameset', 'head',    'html',   'ilayer', 
39362         'iframe', 'layer',  'link',     'meta',    'object',   
39363         'script', 'style' ,'title',  'xml' // clean later..
39364 ];
39365 Roo.form.HtmlEditor.clean = [
39366     'script', 'style', 'title', 'xml'
39367 ];
39368
39369 // attributes..
39370
39371 Roo.form.HtmlEditor.ablack = [
39372     'on'
39373 ];
39374     
39375 Roo.form.HtmlEditor.aclean = [ 
39376     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39377 ];
39378
39379 // protocols..
39380 Roo.form.HtmlEditor.pwhite= [
39381         'http',  'https',  'mailto'
39382 ];
39383
39384 Roo.form.HtmlEditor.cwhite= [
39385         'text-align',
39386         'font-size'
39387 ];
39388
39389 // <script type="text/javascript">
39390 /*
39391  * Based on
39392  * Ext JS Library 1.1.1
39393  * Copyright(c) 2006-2007, Ext JS, LLC.
39394  *  
39395  
39396  */
39397
39398 /**
39399  * @class Roo.form.HtmlEditorToolbar1
39400  * Basic Toolbar
39401  * 
39402  * Usage:
39403  *
39404  new Roo.form.HtmlEditor({
39405     ....
39406     toolbars : [
39407         new Roo.form.HtmlEditorToolbar1({
39408             disable : { fonts: 1 , format: 1, ..., ... , ...],
39409             btns : [ .... ]
39410         })
39411     }
39412      
39413  * 
39414  * @cfg {Object} disable List of elements to disable..
39415  * @cfg {Array} btns List of additional buttons.
39416  * 
39417  * 
39418  * NEEDS Extra CSS? 
39419  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39420  */
39421  
39422 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39423 {
39424     
39425     Roo.apply(this, config);
39426     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39427     // dont call parent... till later.
39428 }
39429
39430 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39431     
39432     tb: false,
39433     
39434     rendered: false,
39435     
39436     editor : false,
39437     /**
39438      * @cfg {Object} disable  List of toolbar elements to disable
39439          
39440      */
39441     disable : false,
39442       /**
39443      * @cfg {Array} fontFamilies An array of available font families
39444      */
39445     fontFamilies : [
39446         'Arial',
39447         'Courier New',
39448         'Tahoma',
39449         'Times New Roman',
39450         'Verdana'
39451     ],
39452     
39453     specialChars : [
39454            "&#169;",
39455           "&#174;",     
39456           "&#8482;",    
39457           "&#163;" ,    
39458          // "&#8212;",    
39459           "&#8230;",    
39460           "&#247;" ,    
39461         //  "&#225;" ,     ?? a acute?
39462            "&#8364;"    , //Euro
39463        //   "&#8220;"    ,
39464         //  "&#8221;"    ,
39465         //  "&#8226;"    ,
39466           "&#176;"  //   , // degrees
39467
39468          // "&#233;"     , // e ecute
39469          // "&#250;"     , // u ecute?
39470     ],
39471     inputElements : [ 
39472             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39473             "input:submit", "input:button", "select", "textarea", "label" ],
39474     formats : [
39475         ["p"] ,  
39476         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39477         ["pre"],[ "code"], 
39478         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39479     ],
39480      /**
39481      * @cfg {String} defaultFont default font to use.
39482      */
39483     defaultFont: 'tahoma',
39484    
39485     fontSelect : false,
39486     
39487     
39488     formatCombo : false,
39489     
39490     init : function(editor)
39491     {
39492         this.editor = editor;
39493         
39494         
39495         var fid = editor.frameId;
39496         var etb = this;
39497         function btn(id, toggle, handler){
39498             var xid = fid + '-'+ id ;
39499             return {
39500                 id : xid,
39501                 cmd : id,
39502                 cls : 'x-btn-icon x-edit-'+id,
39503                 enableToggle:toggle !== false,
39504                 scope: editor, // was editor...
39505                 handler:handler||editor.relayBtnCmd,
39506                 clickEvent:'mousedown',
39507                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39508                 tabIndex:-1
39509             };
39510         }
39511         
39512         
39513         
39514         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39515         this.tb = tb;
39516          // stop form submits
39517         tb.el.on('click', function(e){
39518             e.preventDefault(); // what does this do?
39519         });
39520
39521         if(!this.disable.font && !Roo.isSafari){
39522             /* why no safari for fonts
39523             editor.fontSelect = tb.el.createChild({
39524                 tag:'select',
39525                 tabIndex: -1,
39526                 cls:'x-font-select',
39527                 html: editor.createFontOptions()
39528             });
39529             editor.fontSelect.on('change', function(){
39530                 var font = editor.fontSelect.dom.value;
39531                 editor.relayCmd('fontname', font);
39532                 editor.deferFocus();
39533             }, editor);
39534             tb.add(
39535                 editor.fontSelect.dom,
39536                 '-'
39537             );
39538             */
39539         };
39540         if(!this.disable.formats){
39541             this.formatCombo = new Roo.form.ComboBox({
39542                 store: new Roo.data.SimpleStore({
39543                     id : 'tag',
39544                     fields: ['tag'],
39545                     data : this.formats // from states.js
39546                 }),
39547                 blockFocus : true,
39548                 //autoCreate : {tag: "div",  size: "20"},
39549                 displayField:'tag',
39550                 typeAhead: false,
39551                 mode: 'local',
39552                 editable : false,
39553                 triggerAction: 'all',
39554                 emptyText:'Add tag',
39555                 selectOnFocus:true,
39556                 width:135,
39557                 listeners : {
39558                     'select': function(c, r, i) {
39559                         editor.insertTag(r.get('tag'));
39560                         editor.focus();
39561                     }
39562                 }
39563
39564             });
39565             tb.addField(this.formatCombo);
39566             
39567         }
39568         
39569         if(!this.disable.format){
39570             tb.add(
39571                 btn('bold'),
39572                 btn('italic'),
39573                 btn('underline')
39574             );
39575         };
39576         if(!this.disable.fontSize){
39577             tb.add(
39578                 '-',
39579                 
39580                 
39581                 btn('increasefontsize', false, editor.adjustFont),
39582                 btn('decreasefontsize', false, editor.adjustFont)
39583             );
39584         };
39585         
39586         
39587         if(this.disable.colors){
39588             tb.add(
39589                 '-', {
39590                     id:editor.frameId +'-forecolor',
39591                     cls:'x-btn-icon x-edit-forecolor',
39592                     clickEvent:'mousedown',
39593                     tooltip: this.buttonTips['forecolor'] || undefined,
39594                     tabIndex:-1,
39595                     menu : new Roo.menu.ColorMenu({
39596                         allowReselect: true,
39597                         focus: Roo.emptyFn,
39598                         value:'000000',
39599                         plain:true,
39600                         selectHandler: function(cp, color){
39601                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39602                             editor.deferFocus();
39603                         },
39604                         scope: editor,
39605                         clickEvent:'mousedown'
39606                     })
39607                 }, {
39608                     id:editor.frameId +'backcolor',
39609                     cls:'x-btn-icon x-edit-backcolor',
39610                     clickEvent:'mousedown',
39611                     tooltip: this.buttonTips['backcolor'] || undefined,
39612                     tabIndex:-1,
39613                     menu : new Roo.menu.ColorMenu({
39614                         focus: Roo.emptyFn,
39615                         value:'FFFFFF',
39616                         plain:true,
39617                         allowReselect: true,
39618                         selectHandler: function(cp, color){
39619                             if(Roo.isGecko){
39620                                 editor.execCmd('useCSS', false);
39621                                 editor.execCmd('hilitecolor', color);
39622                                 editor.execCmd('useCSS', true);
39623                                 editor.deferFocus();
39624                             }else{
39625                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39626                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39627                                 editor.deferFocus();
39628                             }
39629                         },
39630                         scope:editor,
39631                         clickEvent:'mousedown'
39632                     })
39633                 }
39634             );
39635         };
39636         // now add all the items...
39637         
39638
39639         if(!this.disable.alignments){
39640             tb.add(
39641                 '-',
39642                 btn('justifyleft'),
39643                 btn('justifycenter'),
39644                 btn('justifyright')
39645             );
39646         };
39647
39648         //if(!Roo.isSafari){
39649             if(!this.disable.links){
39650                 tb.add(
39651                     '-',
39652                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39653                 );
39654             };
39655
39656             if(!this.disable.lists){
39657                 tb.add(
39658                     '-',
39659                     btn('insertorderedlist'),
39660                     btn('insertunorderedlist')
39661                 );
39662             }
39663             if(!this.disable.sourceEdit){
39664                 tb.add(
39665                     '-',
39666                     btn('sourceedit', true, function(btn){
39667                         this.toggleSourceEdit(btn.pressed);
39668                     })
39669                 );
39670             }
39671         //}
39672         
39673         var smenu = { };
39674         // special menu.. - needs to be tidied up..
39675         if (!this.disable.special) {
39676             smenu = {
39677                 text: "&#169;",
39678                 cls: 'x-edit-none',
39679                 menu : {
39680                     items : []
39681                    }
39682             };
39683             for (var i =0; i < this.specialChars.length; i++) {
39684                 smenu.menu.items.push({
39685                     
39686                     html: this.specialChars[i],
39687                     handler: function(a,b) {
39688                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39689                         
39690                     },
39691                     tabIndex:-1
39692                 });
39693             }
39694             
39695             
39696             tb.add(smenu);
39697             
39698             
39699         }
39700         if (this.btns) {
39701             for(var i =0; i< this.btns.length;i++) {
39702                 var b = this.btns[i];
39703                 b.cls =  'x-edit-none';
39704                 b.scope = editor;
39705                 tb.add(b);
39706             }
39707         
39708         }
39709         
39710         
39711         
39712         // disable everything...
39713         
39714         this.tb.items.each(function(item){
39715            if(item.id != editor.frameId+ '-sourceedit'){
39716                 item.disable();
39717             }
39718         });
39719         this.rendered = true;
39720         
39721         // the all the btns;
39722         editor.on('editorevent', this.updateToolbar, this);
39723         // other toolbars need to implement this..
39724         //editor.on('editmodechange', this.updateToolbar, this);
39725     },
39726     
39727     
39728     
39729     /**
39730      * Protected method that will not generally be called directly. It triggers
39731      * a toolbar update by reading the markup state of the current selection in the editor.
39732      */
39733     updateToolbar: function(){
39734
39735         if(!this.editor.activated){
39736             this.editor.onFirstFocus();
39737             return;
39738         }
39739
39740         var btns = this.tb.items.map, 
39741             doc = this.editor.doc,
39742             frameId = this.editor.frameId;
39743
39744         if(!this.disable.font && !Roo.isSafari){
39745             /*
39746             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39747             if(name != this.fontSelect.dom.value){
39748                 this.fontSelect.dom.value = name;
39749             }
39750             */
39751         }
39752         if(!this.disable.format){
39753             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39754             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39755             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39756         }
39757         if(!this.disable.alignments){
39758             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39759             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39760             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39761         }
39762         if(!Roo.isSafari && !this.disable.lists){
39763             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39764             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39765         }
39766         
39767         var ans = this.editor.getAllAncestors();
39768         if (this.formatCombo) {
39769             
39770             
39771             var store = this.formatCombo.store;
39772             this.formatCombo.setValue("");
39773             for (var i =0; i < ans.length;i++) {
39774                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39775                     // select it..
39776                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39777                     break;
39778                 }
39779             }
39780         }
39781         
39782         
39783         
39784         // hides menus... - so this cant be on a menu...
39785         Roo.menu.MenuMgr.hideAll();
39786
39787         //this.editorsyncValue();
39788     },
39789    
39790     
39791     createFontOptions : function(){
39792         var buf = [], fs = this.fontFamilies, ff, lc;
39793         for(var i = 0, len = fs.length; i< len; i++){
39794             ff = fs[i];
39795             lc = ff.toLowerCase();
39796             buf.push(
39797                 '<option value="',lc,'" style="font-family:',ff,';"',
39798                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39799                     ff,
39800                 '</option>'
39801             );
39802         }
39803         return buf.join('');
39804     },
39805     
39806     toggleSourceEdit : function(sourceEditMode){
39807         if(sourceEditMode === undefined){
39808             sourceEditMode = !this.sourceEditMode;
39809         }
39810         this.sourceEditMode = sourceEditMode === true;
39811         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39812         // just toggle the button?
39813         if(btn.pressed !== this.editor.sourceEditMode){
39814             btn.toggle(this.editor.sourceEditMode);
39815             return;
39816         }
39817         
39818         if(this.sourceEditMode){
39819             this.tb.items.each(function(item){
39820                 if(item.cmd != 'sourceedit'){
39821                     item.disable();
39822                 }
39823             });
39824           
39825         }else{
39826             if(this.initialized){
39827                 this.tb.items.each(function(item){
39828                     item.enable();
39829                 });
39830             }
39831             
39832         }
39833         // tell the editor that it's been pressed..
39834         this.editor.toggleSourceEdit(sourceEditMode);
39835        
39836     },
39837      /**
39838      * Object collection of toolbar tooltips for the buttons in the editor. The key
39839      * is the command id associated with that button and the value is a valid QuickTips object.
39840      * For example:
39841 <pre><code>
39842 {
39843     bold : {
39844         title: 'Bold (Ctrl+B)',
39845         text: 'Make the selected text bold.',
39846         cls: 'x-html-editor-tip'
39847     },
39848     italic : {
39849         title: 'Italic (Ctrl+I)',
39850         text: 'Make the selected text italic.',
39851         cls: 'x-html-editor-tip'
39852     },
39853     ...
39854 </code></pre>
39855     * @type Object
39856      */
39857     buttonTips : {
39858         bold : {
39859             title: 'Bold (Ctrl+B)',
39860             text: 'Make the selected text bold.',
39861             cls: 'x-html-editor-tip'
39862         },
39863         italic : {
39864             title: 'Italic (Ctrl+I)',
39865             text: 'Make the selected text italic.',
39866             cls: 'x-html-editor-tip'
39867         },
39868         underline : {
39869             title: 'Underline (Ctrl+U)',
39870             text: 'Underline the selected text.',
39871             cls: 'x-html-editor-tip'
39872         },
39873         increasefontsize : {
39874             title: 'Grow Text',
39875             text: 'Increase the font size.',
39876             cls: 'x-html-editor-tip'
39877         },
39878         decreasefontsize : {
39879             title: 'Shrink Text',
39880             text: 'Decrease the font size.',
39881             cls: 'x-html-editor-tip'
39882         },
39883         backcolor : {
39884             title: 'Text Highlight Color',
39885             text: 'Change the background color of the selected text.',
39886             cls: 'x-html-editor-tip'
39887         },
39888         forecolor : {
39889             title: 'Font Color',
39890             text: 'Change the color of the selected text.',
39891             cls: 'x-html-editor-tip'
39892         },
39893         justifyleft : {
39894             title: 'Align Text Left',
39895             text: 'Align text to the left.',
39896             cls: 'x-html-editor-tip'
39897         },
39898         justifycenter : {
39899             title: 'Center Text',
39900             text: 'Center text in the editor.',
39901             cls: 'x-html-editor-tip'
39902         },
39903         justifyright : {
39904             title: 'Align Text Right',
39905             text: 'Align text to the right.',
39906             cls: 'x-html-editor-tip'
39907         },
39908         insertunorderedlist : {
39909             title: 'Bullet List',
39910             text: 'Start a bulleted list.',
39911             cls: 'x-html-editor-tip'
39912         },
39913         insertorderedlist : {
39914             title: 'Numbered List',
39915             text: 'Start a numbered list.',
39916             cls: 'x-html-editor-tip'
39917         },
39918         createlink : {
39919             title: 'Hyperlink',
39920             text: 'Make the selected text a hyperlink.',
39921             cls: 'x-html-editor-tip'
39922         },
39923         sourceedit : {
39924             title: 'Source Edit',
39925             text: 'Switch to source editing mode.',
39926             cls: 'x-html-editor-tip'
39927         }
39928     },
39929     // private
39930     onDestroy : function(){
39931         if(this.rendered){
39932             
39933             this.tb.items.each(function(item){
39934                 if(item.menu){
39935                     item.menu.removeAll();
39936                     if(item.menu.el){
39937                         item.menu.el.destroy();
39938                     }
39939                 }
39940                 item.destroy();
39941             });
39942              
39943         }
39944     },
39945     onFirstFocus: function() {
39946         this.tb.items.each(function(item){
39947            item.enable();
39948         });
39949     }
39950 });
39951
39952
39953
39954
39955 // <script type="text/javascript">
39956 /*
39957  * Based on
39958  * Ext JS Library 1.1.1
39959  * Copyright(c) 2006-2007, Ext JS, LLC.
39960  *  
39961  
39962  */
39963
39964  
39965 /**
39966  * @class Roo.form.HtmlEditor.ToolbarContext
39967  * Context Toolbar
39968  * 
39969  * Usage:
39970  *
39971  new Roo.form.HtmlEditor({
39972     ....
39973     toolbars : [
39974         new Roo.form.HtmlEditor.ToolbarStandard(),
39975         new Roo.form.HtmlEditor.ToolbarContext()
39976         })
39977     }
39978      
39979  * 
39980  * @config : {Object} disable List of elements to disable.. (not done yet.)
39981  * 
39982  * 
39983  */
39984
39985 Roo.form.HtmlEditor.ToolbarContext = function(config)
39986 {
39987     
39988     Roo.apply(this, config);
39989     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39990     // dont call parent... till later.
39991 }
39992 Roo.form.HtmlEditor.ToolbarContext.types = {
39993     'IMG' : {
39994         width : {
39995             title: "Width",
39996             width: 40
39997         },
39998         height:  {
39999             title: "Height",
40000             width: 40
40001         },
40002         align: {
40003             title: "Align",
40004             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40005             width : 80
40006             
40007         },
40008         border: {
40009             title: "Border",
40010             width: 40
40011         },
40012         alt: {
40013             title: "Alt",
40014             width: 120
40015         },
40016         src : {
40017             title: "Src",
40018             width: 220
40019         }
40020         
40021     },
40022     'A' : {
40023         name : {
40024             title: "Name",
40025             width: 50
40026         },
40027         href:  {
40028             title: "Href",
40029             width: 220
40030         } // border?
40031         
40032     },
40033     'TABLE' : {
40034         rows : {
40035             title: "Rows",
40036             width: 20
40037         },
40038         cols : {
40039             title: "Cols",
40040             width: 20
40041         },
40042         width : {
40043             title: "Width",
40044             width: 40
40045         },
40046         height : {
40047             title: "Height",
40048             width: 40
40049         },
40050         border : {
40051             title: "Border",
40052             width: 20
40053         }
40054     },
40055     'TD' : {
40056         width : {
40057             title: "Width",
40058             width: 40
40059         },
40060         height : {
40061             title: "Height",
40062             width: 40
40063         },   
40064         align: {
40065             title: "Align",
40066             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40067             width: 40
40068         },
40069         valign: {
40070             title: "Valign",
40071             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40072             width: 40
40073         },
40074         colspan: {
40075             title: "Colspan",
40076             width: 20
40077             
40078         }
40079     },
40080     'INPUT' : {
40081         name : {
40082             title: "name",
40083             width: 120
40084         },
40085         value : {
40086             title: "Value",
40087             width: 120
40088         },
40089         width : {
40090             title: "Width",
40091             width: 40
40092         }
40093     },
40094     'LABEL' : {
40095         'for' : {
40096             title: "For",
40097             width: 120
40098         }
40099     },
40100     'TEXTAREA' : {
40101           name : {
40102             title: "name",
40103             width: 120
40104         },
40105         rows : {
40106             title: "Rows",
40107             width: 20
40108         },
40109         cols : {
40110             title: "Cols",
40111             width: 20
40112         }
40113     },
40114     'SELECT' : {
40115         name : {
40116             title: "name",
40117             width: 120
40118         },
40119         selectoptions : {
40120             title: "Options",
40121             width: 200
40122         }
40123     },
40124     'BODY' : {
40125         title : {
40126             title: "title",
40127             width: 120,
40128             disabled : true
40129         }
40130     }
40131 };
40132
40133
40134
40135 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40136     
40137     tb: false,
40138     
40139     rendered: false,
40140     
40141     editor : false,
40142     /**
40143      * @cfg {Object} disable  List of toolbar elements to disable
40144          
40145      */
40146     disable : false,
40147     
40148     
40149     
40150     toolbars : false,
40151     
40152     init : function(editor)
40153     {
40154         this.editor = editor;
40155         
40156         
40157         var fid = editor.frameId;
40158         var etb = this;
40159         function btn(id, toggle, handler){
40160             var xid = fid + '-'+ id ;
40161             return {
40162                 id : xid,
40163                 cmd : id,
40164                 cls : 'x-btn-icon x-edit-'+id,
40165                 enableToggle:toggle !== false,
40166                 scope: editor, // was editor...
40167                 handler:handler||editor.relayBtnCmd,
40168                 clickEvent:'mousedown',
40169                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40170                 tabIndex:-1
40171             };
40172         }
40173         // create a new element.
40174         var wdiv = editor.wrap.createChild({
40175                 tag: 'div'
40176             }, editor.wrap.dom.firstChild.nextSibling, true);
40177         
40178         // can we do this more than once??
40179         
40180          // stop form submits
40181       
40182  
40183         // disable everything...
40184         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40185         this.toolbars = {};
40186            
40187         for (var i in  ty) {
40188           
40189             this.toolbars[i] = this.buildToolbar(ty[i],i);
40190         }
40191         this.tb = this.toolbars.BODY;
40192         this.tb.el.show();
40193         
40194          
40195         this.rendered = true;
40196         
40197         // the all the btns;
40198         editor.on('editorevent', this.updateToolbar, this);
40199         // other toolbars need to implement this..
40200         //editor.on('editmodechange', this.updateToolbar, this);
40201     },
40202     
40203     
40204     
40205     /**
40206      * Protected method that will not generally be called directly. It triggers
40207      * a toolbar update by reading the markup state of the current selection in the editor.
40208      */
40209     updateToolbar: function(){
40210
40211         if(!this.editor.activated){
40212             this.editor.onFirstFocus();
40213             return;
40214         }
40215
40216         
40217         var ans = this.editor.getAllAncestors();
40218         
40219         // pick
40220         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40221         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40222         sel = sel ? sel : this.editor.doc.body;
40223         sel = sel.tagName.length ? sel : this.editor.doc.body;
40224         var tn = sel.tagName.toUpperCase();
40225         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40226         tn = sel.tagName.toUpperCase();
40227         if (this.tb.name  == tn) {
40228             return; // no change
40229         }
40230         this.tb.el.hide();
40231         ///console.log("show: " + tn);
40232         this.tb =  this.toolbars[tn];
40233         this.tb.el.show();
40234         this.tb.fields.each(function(e) {
40235             e.setValue(sel.getAttribute(e.name));
40236         });
40237         this.tb.selectedNode = sel;
40238         
40239         
40240         Roo.menu.MenuMgr.hideAll();
40241
40242         //this.editorsyncValue();
40243     },
40244    
40245        
40246     // private
40247     onDestroy : function(){
40248         if(this.rendered){
40249             
40250             this.tb.items.each(function(item){
40251                 if(item.menu){
40252                     item.menu.removeAll();
40253                     if(item.menu.el){
40254                         item.menu.el.destroy();
40255                     }
40256                 }
40257                 item.destroy();
40258             });
40259              
40260         }
40261     },
40262     onFirstFocus: function() {
40263         // need to do this for all the toolbars..
40264         this.tb.items.each(function(item){
40265            item.enable();
40266         });
40267     },
40268     buildToolbar: function(tlist, nm)
40269     {
40270         var editor = this.editor;
40271          // create a new element.
40272         var wdiv = editor.wrap.createChild({
40273                 tag: 'div'
40274             }, editor.wrap.dom.firstChild.nextSibling, true);
40275         
40276        
40277         var tb = new Roo.Toolbar(wdiv);
40278         tb.add(nm+ ":&nbsp;");
40279         for (var i in tlist) {
40280             var item = tlist[i];
40281             tb.add(item.title + ":&nbsp;");
40282             if (item.opts) {
40283                 // fixme
40284                 
40285               
40286                 tb.addField( new Roo.form.ComboBox({
40287                     store: new Roo.data.SimpleStore({
40288                         id : 'val',
40289                         fields: ['val'],
40290                         data : item.opts // from states.js
40291                     }),
40292                     name : i,
40293                     displayField:'val',
40294                     typeAhead: false,
40295                     mode: 'local',
40296                     editable : false,
40297                     triggerAction: 'all',
40298                     emptyText:'Select',
40299                     selectOnFocus:true,
40300                     width: item.width ? item.width  : 130,
40301                     listeners : {
40302                         'select': function(c, r, i) {
40303                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40304                         }
40305                     }
40306
40307                 }));
40308                 continue;
40309                     
40310                 
40311                 
40312                 
40313                 
40314                 tb.addField( new Roo.form.TextField({
40315                     name: i,
40316                     width: 100,
40317                     //allowBlank:false,
40318                     value: ''
40319                 }));
40320                 continue;
40321             }
40322             tb.addField( new Roo.form.TextField({
40323                 name: i,
40324                 width: item.width,
40325                 //allowBlank:true,
40326                 value: '',
40327                 listeners: {
40328                     'change' : function(f, nv, ov) {
40329                         tb.selectedNode.setAttribute(f.name, nv);
40330                     }
40331                 }
40332             }));
40333              
40334         }
40335         tb.el.on('click', function(e){
40336             e.preventDefault(); // what does this do?
40337         });
40338         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40339         tb.el.hide();
40340         tb.name = nm;
40341         // dont need to disable them... as they will get hidden
40342         return tb;
40343          
40344         
40345     }
40346     
40347     
40348     
40349     
40350 });
40351
40352
40353
40354
40355
40356 /*
40357  * Based on:
40358  * Ext JS Library 1.1.1
40359  * Copyright(c) 2006-2007, Ext JS, LLC.
40360  *
40361  * Originally Released Under LGPL - original licence link has changed is not relivant.
40362  *
40363  * Fork - LGPL
40364  * <script type="text/javascript">
40365  */
40366  
40367 /**
40368  * @class Roo.form.BasicForm
40369  * @extends Roo.util.Observable
40370  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40371  * @constructor
40372  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40373  * @param {Object} config Configuration options
40374  */
40375 Roo.form.BasicForm = function(el, config){
40376     this.allItems = [];
40377     this.childForms = [];
40378     Roo.apply(this, config);
40379     /*
40380      * The Roo.form.Field items in this form.
40381      * @type MixedCollection
40382      */
40383      
40384      
40385     this.items = new Roo.util.MixedCollection(false, function(o){
40386         return o.id || (o.id = Roo.id());
40387     });
40388     this.addEvents({
40389         /**
40390          * @event beforeaction
40391          * Fires before any action is performed. Return false to cancel the action.
40392          * @param {Form} this
40393          * @param {Action} action The action to be performed
40394          */
40395         beforeaction: true,
40396         /**
40397          * @event actionfailed
40398          * Fires when an action fails.
40399          * @param {Form} this
40400          * @param {Action} action The action that failed
40401          */
40402         actionfailed : true,
40403         /**
40404          * @event actioncomplete
40405          * Fires when an action is completed.
40406          * @param {Form} this
40407          * @param {Action} action The action that completed
40408          */
40409         actioncomplete : true
40410     });
40411     if(el){
40412         this.initEl(el);
40413     }
40414     Roo.form.BasicForm.superclass.constructor.call(this);
40415 };
40416
40417 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40418     /**
40419      * @cfg {String} method
40420      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40421      */
40422     /**
40423      * @cfg {DataReader} reader
40424      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40425      * This is optional as there is built-in support for processing JSON.
40426      */
40427     /**
40428      * @cfg {DataReader} errorReader
40429      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40430      * This is completely optional as there is built-in support for processing JSON.
40431      */
40432     /**
40433      * @cfg {String} url
40434      * The URL to use for form actions if one isn't supplied in the action options.
40435      */
40436     /**
40437      * @cfg {Boolean} fileUpload
40438      * Set to true if this form is a file upload.
40439      */
40440      
40441     /**
40442      * @cfg {Object} baseParams
40443      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40444      */
40445      /**
40446      
40447     /**
40448      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40449      */
40450     timeout: 30,
40451
40452     // private
40453     activeAction : null,
40454
40455     /**
40456      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40457      * or setValues() data instead of when the form was first created.
40458      */
40459     trackResetOnLoad : false,
40460     
40461     
40462     /**
40463      * childForms - used for multi-tab forms
40464      * @type {Array}
40465      */
40466     childForms : false,
40467     
40468     /**
40469      * allItems - full list of fields.
40470      * @type {Array}
40471      */
40472     allItems : false,
40473     
40474     /**
40475      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40476      * element by passing it or its id or mask the form itself by passing in true.
40477      * @type Mixed
40478      */
40479     waitMsgTarget : false,
40480
40481     // private
40482     initEl : function(el){
40483         this.el = Roo.get(el);
40484         this.id = this.el.id || Roo.id();
40485         this.el.on('submit', this.onSubmit, this);
40486         this.el.addClass('x-form');
40487     },
40488
40489     // private
40490     onSubmit : function(e){
40491         e.stopEvent();
40492     },
40493
40494     /**
40495      * Returns true if client-side validation on the form is successful.
40496      * @return Boolean
40497      */
40498     isValid : function(){
40499         var valid = true;
40500         this.items.each(function(f){
40501            if(!f.validate()){
40502                valid = false;
40503            }
40504         });
40505         return valid;
40506     },
40507
40508     /**
40509      * Returns true if any fields in this form have changed since their original load.
40510      * @return Boolean
40511      */
40512     isDirty : function(){
40513         var dirty = false;
40514         this.items.each(function(f){
40515            if(f.isDirty()){
40516                dirty = true;
40517                return false;
40518            }
40519         });
40520         return dirty;
40521     },
40522
40523     /**
40524      * Performs a predefined action (submit or load) or custom actions you define on this form.
40525      * @param {String} actionName The name of the action type
40526      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40527      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40528      * accept other config options):
40529      * <pre>
40530 Property          Type             Description
40531 ----------------  ---------------  ----------------------------------------------------------------------------------
40532 url               String           The url for the action (defaults to the form's url)
40533 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40534 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40535 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40536                                    validate the form on the client (defaults to false)
40537      * </pre>
40538      * @return {BasicForm} this
40539      */
40540     doAction : function(action, options){
40541         if(typeof action == 'string'){
40542             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40543         }
40544         if(this.fireEvent('beforeaction', this, action) !== false){
40545             this.beforeAction(action);
40546             action.run.defer(100, action);
40547         }
40548         return this;
40549     },
40550
40551     /**
40552      * Shortcut to do a submit action.
40553      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40554      * @return {BasicForm} this
40555      */
40556     submit : function(options){
40557         this.doAction('submit', options);
40558         return this;
40559     },
40560
40561     /**
40562      * Shortcut to do a load action.
40563      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40564      * @return {BasicForm} this
40565      */
40566     load : function(options){
40567         this.doAction('load', options);
40568         return this;
40569     },
40570
40571     /**
40572      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40573      * @param {Record} record The record to edit
40574      * @return {BasicForm} this
40575      */
40576     updateRecord : function(record){
40577         record.beginEdit();
40578         var fs = record.fields;
40579         fs.each(function(f){
40580             var field = this.findField(f.name);
40581             if(field){
40582                 record.set(f.name, field.getValue());
40583             }
40584         }, this);
40585         record.endEdit();
40586         return this;
40587     },
40588
40589     /**
40590      * Loads an Roo.data.Record into this form.
40591      * @param {Record} record The record to load
40592      * @return {BasicForm} this
40593      */
40594     loadRecord : function(record){
40595         this.setValues(record.data);
40596         return this;
40597     },
40598
40599     // private
40600     beforeAction : function(action){
40601         var o = action.options;
40602         
40603        
40604         if(this.waitMsgTarget === true){
40605             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
40606         }else if(this.waitMsgTarget){
40607             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40608             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
40609         }else {
40610             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
40611         }
40612          
40613     },
40614
40615     // private
40616     afterAction : function(action, success){
40617         this.activeAction = null;
40618         var o = action.options;
40619         
40620         if(this.waitMsgTarget === true){
40621             this.el.unmask();
40622         }else if(this.waitMsgTarget){
40623             this.waitMsgTarget.unmask();
40624         }else{
40625             Roo.MessageBox.updateProgress(1);
40626             Roo.MessageBox.hide();
40627         }
40628          
40629         if(success){
40630             if(o.reset){
40631                 this.reset();
40632             }
40633             Roo.callback(o.success, o.scope, [this, action]);
40634             this.fireEvent('actioncomplete', this, action);
40635             
40636         }else{
40637             Roo.callback(o.failure, o.scope, [this, action]);
40638             // show an error message if no failed handler is set..
40639             if (!this.hasListener('actionfailed')) {
40640                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
40641             }
40642             
40643             this.fireEvent('actionfailed', this, action);
40644         }
40645         
40646     },
40647
40648     /**
40649      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40650      * @param {String} id The value to search for
40651      * @return Field
40652      */
40653     findField : function(id){
40654         var field = this.items.get(id);
40655         if(!field){
40656             this.items.each(function(f){
40657                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40658                     field = f;
40659                     return false;
40660                 }
40661             });
40662         }
40663         return field || null;
40664     },
40665
40666     /**
40667      * Add a secondary form to this one, 
40668      * Used to provide tabbed forms. One form is primary, with hidden values 
40669      * which mirror the elements from the other forms.
40670      * 
40671      * @param {Roo.form.Form} form to add.
40672      * 
40673      */
40674     addForm : function(form)
40675     {
40676        
40677         if (this.childForms.indexOf(form) > -1) {
40678             // already added..
40679             return;
40680         }
40681         this.childForms.push(form);
40682         var n = '';
40683         Roo.each(form.allItems, function (fe) {
40684             
40685             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40686             if (this.findField(n)) { // already added..
40687                 return;
40688             }
40689             var add = new Roo.form.Hidden({
40690                 name : n
40691             });
40692             add.render(this.el);
40693             
40694             this.add( add );
40695         }, this);
40696         
40697     },
40698     /**
40699      * Mark fields in this form invalid in bulk.
40700      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40701      * @return {BasicForm} this
40702      */
40703     markInvalid : function(errors){
40704         if(errors instanceof Array){
40705             for(var i = 0, len = errors.length; i < len; i++){
40706                 var fieldError = errors[i];
40707                 var f = this.findField(fieldError.id);
40708                 if(f){
40709                     f.markInvalid(fieldError.msg);
40710                 }
40711             }
40712         }else{
40713             var field, id;
40714             for(id in errors){
40715                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40716                     field.markInvalid(errors[id]);
40717                 }
40718             }
40719         }
40720         Roo.each(this.childForms || [], function (f) {
40721             f.markInvalid(errors);
40722         });
40723         
40724         return this;
40725     },
40726
40727     /**
40728      * Set values for fields in this form in bulk.
40729      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40730      * @return {BasicForm} this
40731      */
40732     setValues : function(values){
40733         if(values instanceof Array){ // array of objects
40734             for(var i = 0, len = values.length; i < len; i++){
40735                 var v = values[i];
40736                 var f = this.findField(v.id);
40737                 if(f){
40738                     f.setValue(v.value);
40739                     if(this.trackResetOnLoad){
40740                         f.originalValue = f.getValue();
40741                     }
40742                 }
40743             }
40744         }else{ // object hash
40745             var field, id;
40746             for(id in values){
40747                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40748                     
40749                     if (field.setFromData && 
40750                         field.valueField && 
40751                         field.displayField &&
40752                         // combos' with local stores can 
40753                         // be queried via setValue()
40754                         // to set their value..
40755                         (field.store && !field.store.isLocal)
40756                         ) {
40757                         // it's a combo
40758                         var sd = { };
40759                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40760                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40761                         field.setFromData(sd);
40762                         
40763                     } else {
40764                         field.setValue(values[id]);
40765                     }
40766                     
40767                     
40768                     if(this.trackResetOnLoad){
40769                         field.originalValue = field.getValue();
40770                     }
40771                 }
40772             }
40773         }
40774          
40775         Roo.each(this.childForms || [], function (f) {
40776             f.setValues(values);
40777         });
40778                 
40779         return this;
40780     },
40781
40782     /**
40783      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40784      * they are returned as an array.
40785      * @param {Boolean} asString
40786      * @return {Object}
40787      */
40788     getValues : function(asString){
40789         if (this.childForms) {
40790             // copy values from the child forms
40791             Roo.each(this.childForms, function (f) {
40792                 this.setValues(f.getValues());
40793             }, this);
40794         }
40795         
40796         
40797         
40798         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40799         if(asString === true){
40800             return fs;
40801         }
40802         return Roo.urlDecode(fs);
40803     },
40804     
40805     /**
40806      * Returns the fields in this form as an object with key/value pairs. 
40807      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40808      * @return {Object}
40809      */
40810     getFieldValues : function()
40811     {
40812         if (this.childForms) {
40813             // copy values from the child forms
40814             Roo.each(this.childForms, function (f) {
40815                 this.setValues(f.getValues());
40816             }, this);
40817         }
40818         
40819         var ret = {};
40820         this.items.each(function(f){
40821             if (!f.getName()) {
40822                 return;
40823             }
40824             var v = f.getValue();
40825             if ((typeof(v) == 'object') && f.getRawValue) {
40826                 v = f.getRawValue() ; // dates..
40827             }
40828             ret[f.getName()] = v;
40829         });
40830         
40831         return ret;
40832     },
40833
40834     /**
40835      * Clears all invalid messages in this form.
40836      * @return {BasicForm} this
40837      */
40838     clearInvalid : function(){
40839         this.items.each(function(f){
40840            f.clearInvalid();
40841         });
40842         
40843         Roo.each(this.childForms || [], function (f) {
40844             f.clearInvalid();
40845         });
40846         
40847         
40848         return this;
40849     },
40850
40851     /**
40852      * Resets this form.
40853      * @return {BasicForm} this
40854      */
40855     reset : function(){
40856         this.items.each(function(f){
40857             f.reset();
40858         });
40859         
40860         Roo.each(this.childForms || [], function (f) {
40861             f.reset();
40862         });
40863        
40864         
40865         return this;
40866     },
40867
40868     /**
40869      * Add Roo.form components to this form.
40870      * @param {Field} field1
40871      * @param {Field} field2 (optional)
40872      * @param {Field} etc (optional)
40873      * @return {BasicForm} this
40874      */
40875     add : function(){
40876         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40877         return this;
40878     },
40879
40880
40881     /**
40882      * Removes a field from the items collection (does NOT remove its markup).
40883      * @param {Field} field
40884      * @return {BasicForm} this
40885      */
40886     remove : function(field){
40887         this.items.remove(field);
40888         return this;
40889     },
40890
40891     /**
40892      * Looks at the fields in this form, checks them for an id attribute,
40893      * and calls applyTo on the existing dom element with that id.
40894      * @return {BasicForm} this
40895      */
40896     render : function(){
40897         this.items.each(function(f){
40898             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40899                 f.applyTo(f.id);
40900             }
40901         });
40902         return this;
40903     },
40904
40905     /**
40906      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40907      * @param {Object} values
40908      * @return {BasicForm} this
40909      */
40910     applyToFields : function(o){
40911         this.items.each(function(f){
40912            Roo.apply(f, o);
40913         });
40914         return this;
40915     },
40916
40917     /**
40918      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40919      * @param {Object} values
40920      * @return {BasicForm} this
40921      */
40922     applyIfToFields : function(o){
40923         this.items.each(function(f){
40924            Roo.applyIf(f, o);
40925         });
40926         return this;
40927     }
40928 });
40929
40930 // back compat
40931 Roo.BasicForm = Roo.form.BasicForm;/*
40932  * Based on:
40933  * Ext JS Library 1.1.1
40934  * Copyright(c) 2006-2007, Ext JS, LLC.
40935  *
40936  * Originally Released Under LGPL - original licence link has changed is not relivant.
40937  *
40938  * Fork - LGPL
40939  * <script type="text/javascript">
40940  */
40941
40942 /**
40943  * @class Roo.form.Form
40944  * @extends Roo.form.BasicForm
40945  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40946  * @constructor
40947  * @param {Object} config Configuration options
40948  */
40949 Roo.form.Form = function(config){
40950     var xitems =  [];
40951     if (config.items) {
40952         xitems = config.items;
40953         delete config.items;
40954     }
40955    
40956     
40957     Roo.form.Form.superclass.constructor.call(this, null, config);
40958     this.url = this.url || this.action;
40959     if(!this.root){
40960         this.root = new Roo.form.Layout(Roo.applyIf({
40961             id: Roo.id()
40962         }, config));
40963     }
40964     this.active = this.root;
40965     /**
40966      * Array of all the buttons that have been added to this form via {@link addButton}
40967      * @type Array
40968      */
40969     this.buttons = [];
40970     this.allItems = [];
40971     this.addEvents({
40972         /**
40973          * @event clientvalidation
40974          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40975          * @param {Form} this
40976          * @param {Boolean} valid true if the form has passed client-side validation
40977          */
40978         clientvalidation: true,
40979         /**
40980          * @event rendered
40981          * Fires when the form is rendered
40982          * @param {Roo.form.Form} form
40983          */
40984         rendered : true
40985     });
40986     
40987     if (this.progressUrl) {
40988             // push a hidden field onto the list of fields..
40989             this.addxtype( {
40990                     xns: Roo.form, 
40991                     xtype : 'Hidden', 
40992                     name : 'UPLOAD_IDENTIFIER' 
40993             });
40994         }
40995         
40996     
40997     Roo.each(xitems, this.addxtype, this);
40998     
40999     
41000     
41001 };
41002
41003 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41004     /**
41005      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41006      */
41007     /**
41008      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41009      */
41010     /**
41011      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41012      */
41013     buttonAlign:'center',
41014
41015     /**
41016      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41017      */
41018     minButtonWidth:75,
41019
41020     /**
41021      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41022      * This property cascades to child containers if not set.
41023      */
41024     labelAlign:'left',
41025
41026     /**
41027      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41028      * fires a looping event with that state. This is required to bind buttons to the valid
41029      * state using the config value formBind:true on the button.
41030      */
41031     monitorValid : false,
41032
41033     /**
41034      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41035      */
41036     monitorPoll : 200,
41037     
41038     /**
41039      * @cfg {String} progressUrl - Url to return progress data 
41040      */
41041     
41042     progressUrl : false,
41043   
41044     /**
41045      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41046      * fields are added and the column is closed. If no fields are passed the column remains open
41047      * until end() is called.
41048      * @param {Object} config The config to pass to the column
41049      * @param {Field} field1 (optional)
41050      * @param {Field} field2 (optional)
41051      * @param {Field} etc (optional)
41052      * @return Column The column container object
41053      */
41054     column : function(c){
41055         var col = new Roo.form.Column(c);
41056         this.start(col);
41057         if(arguments.length > 1){ // duplicate code required because of Opera
41058             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41059             this.end();
41060         }
41061         return col;
41062     },
41063
41064     /**
41065      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41066      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41067      * until end() is called.
41068      * @param {Object} config The config to pass to the fieldset
41069      * @param {Field} field1 (optional)
41070      * @param {Field} field2 (optional)
41071      * @param {Field} etc (optional)
41072      * @return FieldSet The fieldset container object
41073      */
41074     fieldset : function(c){
41075         var fs = new Roo.form.FieldSet(c);
41076         this.start(fs);
41077         if(arguments.length > 1){ // duplicate code required because of Opera
41078             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41079             this.end();
41080         }
41081         return fs;
41082     },
41083
41084     /**
41085      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41086      * fields are added and the container is closed. If no fields are passed the container remains open
41087      * until end() is called.
41088      * @param {Object} config The config to pass to the Layout
41089      * @param {Field} field1 (optional)
41090      * @param {Field} field2 (optional)
41091      * @param {Field} etc (optional)
41092      * @return Layout The container object
41093      */
41094     container : function(c){
41095         var l = new Roo.form.Layout(c);
41096         this.start(l);
41097         if(arguments.length > 1){ // duplicate code required because of Opera
41098             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41099             this.end();
41100         }
41101         return l;
41102     },
41103
41104     /**
41105      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41106      * @param {Object} container A Roo.form.Layout or subclass of Layout
41107      * @return {Form} this
41108      */
41109     start : function(c){
41110         // cascade label info
41111         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41112         this.active.stack.push(c);
41113         c.ownerCt = this.active;
41114         this.active = c;
41115         return this;
41116     },
41117
41118     /**
41119      * Closes the current open container
41120      * @return {Form} this
41121      */
41122     end : function(){
41123         if(this.active == this.root){
41124             return this;
41125         }
41126         this.active = this.active.ownerCt;
41127         return this;
41128     },
41129
41130     /**
41131      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41132      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41133      * as the label of the field.
41134      * @param {Field} field1
41135      * @param {Field} field2 (optional)
41136      * @param {Field} etc. (optional)
41137      * @return {Form} this
41138      */
41139     add : function(){
41140         this.active.stack.push.apply(this.active.stack, arguments);
41141         this.allItems.push.apply(this.allItems,arguments);
41142         var r = [];
41143         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41144             if(a[i].isFormField){
41145                 r.push(a[i]);
41146             }
41147         }
41148         if(r.length > 0){
41149             Roo.form.Form.superclass.add.apply(this, r);
41150         }
41151         return this;
41152     },
41153     
41154
41155     
41156     
41157     
41158      /**
41159      * Find any element that has been added to a form, using it's ID or name
41160      * This can include framesets, columns etc. along with regular fields..
41161      * @param {String} id - id or name to find.
41162      
41163      * @return {Element} e - or false if nothing found.
41164      */
41165     findbyId : function(id)
41166     {
41167         var ret = false;
41168         if (!id) {
41169             return ret;
41170         }
41171         Ext.each(this.allItems, function(f){
41172             if (f.id == id || f.name == id ){
41173                 ret = f;
41174                 return false;
41175             }
41176         });
41177         return ret;
41178     },
41179
41180     
41181     
41182     /**
41183      * Render this form into the passed container. This should only be called once!
41184      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41185      * @return {Form} this
41186      */
41187     render : function(ct)
41188     {
41189         
41190         
41191         
41192         ct = Roo.get(ct);
41193         var o = this.autoCreate || {
41194             tag: 'form',
41195             method : this.method || 'POST',
41196             id : this.id || Roo.id()
41197         };
41198         this.initEl(ct.createChild(o));
41199
41200         this.root.render(this.el);
41201         
41202        
41203              
41204         this.items.each(function(f){
41205             f.render('x-form-el-'+f.id);
41206         });
41207
41208         if(this.buttons.length > 0){
41209             // tables are required to maintain order and for correct IE layout
41210             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41211                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41212                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41213             }}, null, true);
41214             var tr = tb.getElementsByTagName('tr')[0];
41215             for(var i = 0, len = this.buttons.length; i < len; i++) {
41216                 var b = this.buttons[i];
41217                 var td = document.createElement('td');
41218                 td.className = 'x-form-btn-td';
41219                 b.render(tr.appendChild(td));
41220             }
41221         }
41222         if(this.monitorValid){ // initialize after render
41223             this.startMonitoring();
41224         }
41225         this.fireEvent('rendered', this);
41226         return this;
41227     },
41228
41229     /**
41230      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41231      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41232      * object or a valid Roo.DomHelper element config
41233      * @param {Function} handler The function called when the button is clicked
41234      * @param {Object} scope (optional) The scope of the handler function
41235      * @return {Roo.Button}
41236      */
41237     addButton : function(config, handler, scope){
41238         var bc = {
41239             handler: handler,
41240             scope: scope,
41241             minWidth: this.minButtonWidth,
41242             hideParent:true
41243         };
41244         if(typeof config == "string"){
41245             bc.text = config;
41246         }else{
41247             Roo.apply(bc, config);
41248         }
41249         var btn = new Roo.Button(null, bc);
41250         this.buttons.push(btn);
41251         return btn;
41252     },
41253
41254      /**
41255      * Adds a series of form elements (using the xtype property as the factory method.
41256      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41257      * @param {Object} config 
41258      */
41259     
41260     addxtype : function()
41261     {
41262         var ar = Array.prototype.slice.call(arguments, 0);
41263         var ret = false;
41264         for(var i = 0; i < ar.length; i++) {
41265             if (!ar[i]) {
41266                 continue; // skip -- if this happends something invalid got sent, we 
41267                 // should ignore it, as basically that interface element will not show up
41268                 // and that should be pretty obvious!!
41269             }
41270             
41271             if (Roo.form[ar[i].xtype]) {
41272                 ar[i].form = this;
41273                 var fe = Roo.factory(ar[i], Roo.form);
41274                 if (!ret) {
41275                     ret = fe;
41276                 }
41277                 fe.form = this;
41278                 if (fe.store) {
41279                     fe.store.form = this;
41280                 }
41281                 if (fe.isLayout) {  
41282                          
41283                     this.start(fe);
41284                     this.allItems.push(fe);
41285                     if (fe.items && fe.addxtype) {
41286                         fe.addxtype.apply(fe, fe.items);
41287                         delete fe.items;
41288                     }
41289                      this.end();
41290                     continue;
41291                 }
41292                 
41293                 
41294                  
41295                 this.add(fe);
41296               //  console.log('adding ' + ar[i].xtype);
41297             }
41298             if (ar[i].xtype == 'Button') {  
41299                 //console.log('adding button');
41300                 //console.log(ar[i]);
41301                 this.addButton(ar[i]);
41302                 this.allItems.push(fe);
41303                 continue;
41304             }
41305             
41306             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41307                 alert('end is not supported on xtype any more, use items');
41308             //    this.end();
41309             //    //console.log('adding end');
41310             }
41311             
41312         }
41313         return ret;
41314     },
41315     
41316     /**
41317      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41318      * option "monitorValid"
41319      */
41320     startMonitoring : function(){
41321         if(!this.bound){
41322             this.bound = true;
41323             Roo.TaskMgr.start({
41324                 run : this.bindHandler,
41325                 interval : this.monitorPoll || 200,
41326                 scope: this
41327             });
41328         }
41329     },
41330
41331     /**
41332      * Stops monitoring of the valid state of this form
41333      */
41334     stopMonitoring : function(){
41335         this.bound = false;
41336     },
41337
41338     // private
41339     bindHandler : function(){
41340         if(!this.bound){
41341             return false; // stops binding
41342         }
41343         var valid = true;
41344         this.items.each(function(f){
41345             if(!f.isValid(true)){
41346                 valid = false;
41347                 return false;
41348             }
41349         });
41350         for(var i = 0, len = this.buttons.length; i < len; i++){
41351             var btn = this.buttons[i];
41352             if(btn.formBind === true && btn.disabled === valid){
41353                 btn.setDisabled(!valid);
41354             }
41355         }
41356         this.fireEvent('clientvalidation', this, valid);
41357     }
41358     
41359     
41360     
41361     
41362     
41363     
41364     
41365     
41366 });
41367
41368
41369 // back compat
41370 Roo.Form = Roo.form.Form;
41371 /*
41372  * Based on:
41373  * Ext JS Library 1.1.1
41374  * Copyright(c) 2006-2007, Ext JS, LLC.
41375  *
41376  * Originally Released Under LGPL - original licence link has changed is not relivant.
41377  *
41378  * Fork - LGPL
41379  * <script type="text/javascript">
41380  */
41381  
41382  /**
41383  * @class Roo.form.Action
41384  * Internal Class used to handle form actions
41385  * @constructor
41386  * @param {Roo.form.BasicForm} el The form element or its id
41387  * @param {Object} config Configuration options
41388  */
41389  
41390  
41391 // define the action interface
41392 Roo.form.Action = function(form, options){
41393     this.form = form;
41394     this.options = options || {};
41395 };
41396 /**
41397  * Client Validation Failed
41398  * @const 
41399  */
41400 Roo.form.Action.CLIENT_INVALID = 'client';
41401 /**
41402  * Server Validation Failed
41403  * @const 
41404  */
41405  Roo.form.Action.SERVER_INVALID = 'server';
41406  /**
41407  * Connect to Server Failed
41408  * @const 
41409  */
41410 Roo.form.Action.CONNECT_FAILURE = 'connect';
41411 /**
41412  * Reading Data from Server Failed
41413  * @const 
41414  */
41415 Roo.form.Action.LOAD_FAILURE = 'load';
41416
41417 Roo.form.Action.prototype = {
41418     type : 'default',
41419     failureType : undefined,
41420     response : undefined,
41421     result : undefined,
41422
41423     // interface method
41424     run : function(options){
41425
41426     },
41427
41428     // interface method
41429     success : function(response){
41430
41431     },
41432
41433     // interface method
41434     handleResponse : function(response){
41435
41436     },
41437
41438     // default connection failure
41439     failure : function(response){
41440         
41441         this.response = response;
41442         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41443         this.form.afterAction(this, false);
41444     },
41445
41446     processResponse : function(response){
41447         this.response = response;
41448         if(!response.responseText){
41449             return true;
41450         }
41451         this.result = this.handleResponse(response);
41452         return this.result;
41453     },
41454
41455     // utility functions used internally
41456     getUrl : function(appendParams){
41457         var url = this.options.url || this.form.url || this.form.el.dom.action;
41458         if(appendParams){
41459             var p = this.getParams();
41460             if(p){
41461                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41462             }
41463         }
41464         return url;
41465     },
41466
41467     getMethod : function(){
41468         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41469     },
41470
41471     getParams : function(){
41472         var bp = this.form.baseParams;
41473         var p = this.options.params;
41474         if(p){
41475             if(typeof p == "object"){
41476                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41477             }else if(typeof p == 'string' && bp){
41478                 p += '&' + Roo.urlEncode(bp);
41479             }
41480         }else if(bp){
41481             p = Roo.urlEncode(bp);
41482         }
41483         return p;
41484     },
41485
41486     createCallback : function(){
41487         return {
41488             success: this.success,
41489             failure: this.failure,
41490             scope: this,
41491             timeout: (this.form.timeout*1000),
41492             upload: this.form.fileUpload ? this.success : undefined
41493         };
41494     }
41495 };
41496
41497 Roo.form.Action.Submit = function(form, options){
41498     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41499 };
41500
41501 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41502     type : 'submit',
41503
41504     haveProgress : false,
41505     uploadComplete : false,
41506     
41507     // uploadProgress indicator.
41508     uploadProgress : function()
41509     {
41510         if (!this.form.progressUrl) {
41511             return;
41512         }
41513         
41514         if (!this.haveProgress) {
41515             Roo.MessageBox.progress("Uploading", "Uploading");
41516         }
41517         if (this.uploadComplete) {
41518            Roo.MessageBox.hide();
41519            return;
41520         }
41521         
41522         this.haveProgress = true;
41523    
41524         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
41525         
41526         var c = new Roo.data.Connection();
41527         c.request({
41528             url : this.form.progressUrl,
41529             params: {
41530                 id : uid
41531             },
41532             method: 'GET',
41533             success : function(req){
41534                //console.log(data);
41535                 var rdata = false;
41536                 var edata;
41537                 try  {
41538                    rdata = Roo.decode(req.responseText)
41539                 } catch (e) {
41540                     Roo.log("Invalid data from server..");
41541                     Roo.log(edata);
41542                     return;
41543                 }
41544                 if (!rdata || !rdata.success) {
41545                     Roo.log(rdata);
41546                     return;
41547                 }
41548                 var data = rdata.data;
41549                 
41550                 if (this.uploadComplete) {
41551                    Roo.MessageBox.hide();
41552                    return;
41553                 }
41554                    
41555                 if (data){
41556                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
41557                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
41558                     );
41559                 }
41560                 this.uploadProgress.defer(2000,this);
41561             },
41562        
41563             failure: function(data) {
41564                 Roo.log('progress url failed ');
41565                 Roo.log(data);
41566             },
41567             scope : this
41568         });
41569            
41570     },
41571     
41572     
41573     run : function()
41574     {
41575         // run get Values on the form, so it syncs any secondary forms.
41576         this.form.getValues();
41577         
41578         var o = this.options;
41579         var method = this.getMethod();
41580         var isPost = method == 'POST';
41581         if(o.clientValidation === false || this.form.isValid()){
41582             
41583             if (this.form.progressUrl) {
41584                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
41585                     (new Date() * 1) + '' + Math.random());
41586                     
41587             } 
41588             
41589             
41590             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41591                 form:this.form.el.dom,
41592                 url:this.getUrl(!isPost),
41593                 method: method,
41594                 params:isPost ? this.getParams() : null,
41595                 isUpload: this.form.fileUpload
41596             }));
41597             
41598             this.uploadProgress();
41599
41600         }else if (o.clientValidation !== false){ // client validation failed
41601             this.failureType = Roo.form.Action.CLIENT_INVALID;
41602             this.form.afterAction(this, false);
41603         }
41604     },
41605
41606     success : function(response)
41607     {
41608         this.uploadComplete= true;
41609         if (this.haveProgress) {
41610             Roo.MessageBox.hide();
41611         }
41612         
41613         
41614         var result = this.processResponse(response);
41615         if(result === true || result.success){
41616             this.form.afterAction(this, true);
41617             return;
41618         }
41619         if(result.errors){
41620             this.form.markInvalid(result.errors);
41621             this.failureType = Roo.form.Action.SERVER_INVALID;
41622         }
41623         this.form.afterAction(this, false);
41624     },
41625     failure : function(response)
41626     {
41627         this.uploadComplete= true;
41628         if (this.haveProgress) {
41629             Roo.MessageBox.hide();
41630         }
41631         
41632         
41633         this.response = response;
41634         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41635         this.form.afterAction(this, false);
41636     },
41637     
41638     handleResponse : function(response){
41639         if(this.form.errorReader){
41640             var rs = this.form.errorReader.read(response);
41641             var errors = [];
41642             if(rs.records){
41643                 for(var i = 0, len = rs.records.length; i < len; i++) {
41644                     var r = rs.records[i];
41645                     errors[i] = r.data;
41646                 }
41647             }
41648             if(errors.length < 1){
41649                 errors = null;
41650             }
41651             return {
41652                 success : rs.success,
41653                 errors : errors
41654             };
41655         }
41656         var ret = false;
41657         try {
41658             ret = Roo.decode(response.responseText);
41659         } catch (e) {
41660             ret = {
41661                 success: false,
41662                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41663                 errors : []
41664             };
41665         }
41666         return ret;
41667         
41668     }
41669 });
41670
41671
41672 Roo.form.Action.Load = function(form, options){
41673     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41674     this.reader = this.form.reader;
41675 };
41676
41677 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41678     type : 'load',
41679
41680     run : function(){
41681         
41682         Roo.Ajax.request(Roo.apply(
41683                 this.createCallback(), {
41684                     method:this.getMethod(),
41685                     url:this.getUrl(false),
41686                     params:this.getParams()
41687         }));
41688     },
41689
41690     success : function(response){
41691         
41692         var result = this.processResponse(response);
41693         if(result === true || !result.success || !result.data){
41694             this.failureType = Roo.form.Action.LOAD_FAILURE;
41695             this.form.afterAction(this, false);
41696             return;
41697         }
41698         this.form.clearInvalid();
41699         this.form.setValues(result.data);
41700         this.form.afterAction(this, true);
41701     },
41702
41703     handleResponse : function(response){
41704         if(this.form.reader){
41705             var rs = this.form.reader.read(response);
41706             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41707             return {
41708                 success : rs.success,
41709                 data : data
41710             };
41711         }
41712         return Roo.decode(response.responseText);
41713     }
41714 });
41715
41716 Roo.form.Action.ACTION_TYPES = {
41717     'load' : Roo.form.Action.Load,
41718     'submit' : Roo.form.Action.Submit
41719 };/*
41720  * Based on:
41721  * Ext JS Library 1.1.1
41722  * Copyright(c) 2006-2007, Ext JS, LLC.
41723  *
41724  * Originally Released Under LGPL - original licence link has changed is not relivant.
41725  *
41726  * Fork - LGPL
41727  * <script type="text/javascript">
41728  */
41729  
41730 /**
41731  * @class Roo.form.Layout
41732  * @extends Roo.Component
41733  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41734  * @constructor
41735  * @param {Object} config Configuration options
41736  */
41737 Roo.form.Layout = function(config){
41738     var xitems = [];
41739     if (config.items) {
41740         xitems = config.items;
41741         delete config.items;
41742     }
41743     Roo.form.Layout.superclass.constructor.call(this, config);
41744     this.stack = [];
41745     Roo.each(xitems, this.addxtype, this);
41746      
41747 };
41748
41749 Roo.extend(Roo.form.Layout, Roo.Component, {
41750     /**
41751      * @cfg {String/Object} autoCreate
41752      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41753      */
41754     /**
41755      * @cfg {String/Object/Function} style
41756      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41757      * a function which returns such a specification.
41758      */
41759     /**
41760      * @cfg {String} labelAlign
41761      * Valid values are "left," "top" and "right" (defaults to "left")
41762      */
41763     /**
41764      * @cfg {Number} labelWidth
41765      * Fixed width in pixels of all field labels (defaults to undefined)
41766      */
41767     /**
41768      * @cfg {Boolean} clear
41769      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41770      */
41771     clear : true,
41772     /**
41773      * @cfg {String} labelSeparator
41774      * The separator to use after field labels (defaults to ':')
41775      */
41776     labelSeparator : ':',
41777     /**
41778      * @cfg {Boolean} hideLabels
41779      * True to suppress the display of field labels in this layout (defaults to false)
41780      */
41781     hideLabels : false,
41782
41783     // private
41784     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41785     
41786     isLayout : true,
41787     
41788     // private
41789     onRender : function(ct, position){
41790         if(this.el){ // from markup
41791             this.el = Roo.get(this.el);
41792         }else {  // generate
41793             var cfg = this.getAutoCreate();
41794             this.el = ct.createChild(cfg, position);
41795         }
41796         if(this.style){
41797             this.el.applyStyles(this.style);
41798         }
41799         if(this.labelAlign){
41800             this.el.addClass('x-form-label-'+this.labelAlign);
41801         }
41802         if(this.hideLabels){
41803             this.labelStyle = "display:none";
41804             this.elementStyle = "padding-left:0;";
41805         }else{
41806             if(typeof this.labelWidth == 'number'){
41807                 this.labelStyle = "width:"+this.labelWidth+"px;";
41808                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41809             }
41810             if(this.labelAlign == 'top'){
41811                 this.labelStyle = "width:auto;";
41812                 this.elementStyle = "padding-left:0;";
41813             }
41814         }
41815         var stack = this.stack;
41816         var slen = stack.length;
41817         if(slen > 0){
41818             if(!this.fieldTpl){
41819                 var t = new Roo.Template(
41820                     '<div class="x-form-item {5}">',
41821                         '<label for="{0}" style="{2}">{1}{4}</label>',
41822                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41823                         '</div>',
41824                     '</div><div class="x-form-clear-left"></div>'
41825                 );
41826                 t.disableFormats = true;
41827                 t.compile();
41828                 Roo.form.Layout.prototype.fieldTpl = t;
41829             }
41830             for(var i = 0; i < slen; i++) {
41831                 if(stack[i].isFormField){
41832                     this.renderField(stack[i]);
41833                 }else{
41834                     this.renderComponent(stack[i]);
41835                 }
41836             }
41837         }
41838         if(this.clear){
41839             this.el.createChild({cls:'x-form-clear'});
41840         }
41841     },
41842
41843     // private
41844     renderField : function(f){
41845         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41846                f.id, //0
41847                f.fieldLabel, //1
41848                f.labelStyle||this.labelStyle||'', //2
41849                this.elementStyle||'', //3
41850                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41851                f.itemCls||this.itemCls||''  //5
41852        ], true).getPrevSibling());
41853     },
41854
41855     // private
41856     renderComponent : function(c){
41857         c.render(c.isLayout ? this.el : this.el.createChild());    
41858     },
41859     /**
41860      * Adds a object form elements (using the xtype property as the factory method.)
41861      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41862      * @param {Object} config 
41863      */
41864     addxtype : function(o)
41865     {
41866         // create the lement.
41867         o.form = this.form;
41868         var fe = Roo.factory(o, Roo.form);
41869         this.form.allItems.push(fe);
41870         this.stack.push(fe);
41871         
41872         if (fe.isFormField) {
41873             this.form.items.add(fe);
41874         }
41875          
41876         return fe;
41877     }
41878 });
41879
41880 /**
41881  * @class Roo.form.Column
41882  * @extends Roo.form.Layout
41883  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41884  * @constructor
41885  * @param {Object} config Configuration options
41886  */
41887 Roo.form.Column = function(config){
41888     Roo.form.Column.superclass.constructor.call(this, config);
41889 };
41890
41891 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41892     /**
41893      * @cfg {Number/String} width
41894      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41895      */
41896     /**
41897      * @cfg {String/Object} autoCreate
41898      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41899      */
41900
41901     // private
41902     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41903
41904     // private
41905     onRender : function(ct, position){
41906         Roo.form.Column.superclass.onRender.call(this, ct, position);
41907         if(this.width){
41908             this.el.setWidth(this.width);
41909         }
41910     }
41911 });
41912
41913
41914 /**
41915  * @class Roo.form.Row
41916  * @extends Roo.form.Layout
41917  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41918  * @constructor
41919  * @param {Object} config Configuration options
41920  */
41921
41922  
41923 Roo.form.Row = function(config){
41924     Roo.form.Row.superclass.constructor.call(this, config);
41925 };
41926  
41927 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41928       /**
41929      * @cfg {Number/String} width
41930      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41931      */
41932     /**
41933      * @cfg {Number/String} height
41934      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41935      */
41936     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41937     
41938     padWidth : 20,
41939     // private
41940     onRender : function(ct, position){
41941         //console.log('row render');
41942         if(!this.rowTpl){
41943             var t = new Roo.Template(
41944                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41945                     '<label for="{0}" style="{2}">{1}{4}</label>',
41946                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41947                     '</div>',
41948                 '</div>'
41949             );
41950             t.disableFormats = true;
41951             t.compile();
41952             Roo.form.Layout.prototype.rowTpl = t;
41953         }
41954         this.fieldTpl = this.rowTpl;
41955         
41956         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41957         var labelWidth = 100;
41958         
41959         if ((this.labelAlign != 'top')) {
41960             if (typeof this.labelWidth == 'number') {
41961                 labelWidth = this.labelWidth
41962             }
41963             this.padWidth =  20 + labelWidth;
41964             
41965         }
41966         
41967         Roo.form.Column.superclass.onRender.call(this, ct, position);
41968         if(this.width){
41969             this.el.setWidth(this.width);
41970         }
41971         if(this.height){
41972             this.el.setHeight(this.height);
41973         }
41974     },
41975     
41976     // private
41977     renderField : function(f){
41978         f.fieldEl = this.fieldTpl.append(this.el, [
41979                f.id, f.fieldLabel,
41980                f.labelStyle||this.labelStyle||'',
41981                this.elementStyle||'',
41982                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
41983                f.itemCls||this.itemCls||'',
41984                f.width ? f.width + this.padWidth : 160 + this.padWidth
41985        ],true);
41986     }
41987 });
41988  
41989
41990 /**
41991  * @class Roo.form.FieldSet
41992  * @extends Roo.form.Layout
41993  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
41994  * @constructor
41995  * @param {Object} config Configuration options
41996  */
41997 Roo.form.FieldSet = function(config){
41998     Roo.form.FieldSet.superclass.constructor.call(this, config);
41999 };
42000
42001 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42002     /**
42003      * @cfg {String} legend
42004      * The text to display as the legend for the FieldSet (defaults to '')
42005      */
42006     /**
42007      * @cfg {String/Object} autoCreate
42008      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42009      */
42010
42011     // private
42012     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42013
42014     // private
42015     onRender : function(ct, position){
42016         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42017         if(this.legend){
42018             this.setLegend(this.legend);
42019         }
42020     },
42021
42022     // private
42023     setLegend : function(text){
42024         if(this.rendered){
42025             this.el.child('legend').update(text);
42026         }
42027     }
42028 });/*
42029  * Based on:
42030  * Ext JS Library 1.1.1
42031  * Copyright(c) 2006-2007, Ext JS, LLC.
42032  *
42033  * Originally Released Under LGPL - original licence link has changed is not relivant.
42034  *
42035  * Fork - LGPL
42036  * <script type="text/javascript">
42037  */
42038 /**
42039  * @class Roo.form.VTypes
42040  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42041  * @singleton
42042  */
42043 Roo.form.VTypes = function(){
42044     // closure these in so they are only created once.
42045     var alpha = /^[a-zA-Z_]+$/;
42046     var alphanum = /^[a-zA-Z0-9_]+$/;
42047     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42048     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42049
42050     // All these messages and functions are configurable
42051     return {
42052         /**
42053          * The function used to validate email addresses
42054          * @param {String} value The email address
42055          */
42056         'email' : function(v){
42057             return email.test(v);
42058         },
42059         /**
42060          * The error text to display when the email validation function returns false
42061          * @type String
42062          */
42063         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42064         /**
42065          * The keystroke filter mask to be applied on email input
42066          * @type RegExp
42067          */
42068         'emailMask' : /[a-z0-9_\.\-@]/i,
42069
42070         /**
42071          * The function used to validate URLs
42072          * @param {String} value The URL
42073          */
42074         'url' : function(v){
42075             return url.test(v);
42076         },
42077         /**
42078          * The error text to display when the url validation function returns false
42079          * @type String
42080          */
42081         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42082         
42083         /**
42084          * The function used to validate alpha values
42085          * @param {String} value The value
42086          */
42087         'alpha' : function(v){
42088             return alpha.test(v);
42089         },
42090         /**
42091          * The error text to display when the alpha validation function returns false
42092          * @type String
42093          */
42094         'alphaText' : 'This field should only contain letters and _',
42095         /**
42096          * The keystroke filter mask to be applied on alpha input
42097          * @type RegExp
42098          */
42099         'alphaMask' : /[a-z_]/i,
42100
42101         /**
42102          * The function used to validate alphanumeric values
42103          * @param {String} value The value
42104          */
42105         'alphanum' : function(v){
42106             return alphanum.test(v);
42107         },
42108         /**
42109          * The error text to display when the alphanumeric validation function returns false
42110          * @type String
42111          */
42112         'alphanumText' : 'This field should only contain letters, numbers and _',
42113         /**
42114          * The keystroke filter mask to be applied on alphanumeric input
42115          * @type RegExp
42116          */
42117         'alphanumMask' : /[a-z0-9_]/i
42118     };
42119 }();//<script type="text/javascript">
42120
42121 /**
42122  * @class Roo.form.FCKeditor
42123  * @extends Roo.form.TextArea
42124  * Wrapper around the FCKEditor http://www.fckeditor.net
42125  * @constructor
42126  * Creates a new FCKeditor
42127  * @param {Object} config Configuration options
42128  */
42129 Roo.form.FCKeditor = function(config){
42130     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42131     this.addEvents({
42132          /**
42133          * @event editorinit
42134          * Fired when the editor is initialized - you can add extra handlers here..
42135          * @param {FCKeditor} this
42136          * @param {Object} the FCK object.
42137          */
42138         editorinit : true
42139     });
42140     
42141     
42142 };
42143 Roo.form.FCKeditor.editors = { };
42144 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42145 {
42146     //defaultAutoCreate : {
42147     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42148     //},
42149     // private
42150     /**
42151      * @cfg {Object} fck options - see fck manual for details.
42152      */
42153     fckconfig : false,
42154     
42155     /**
42156      * @cfg {Object} fck toolbar set (Basic or Default)
42157      */
42158     toolbarSet : 'Basic',
42159     /**
42160      * @cfg {Object} fck BasePath
42161      */ 
42162     basePath : '/fckeditor/',
42163     
42164     
42165     frame : false,
42166     
42167     value : '',
42168     
42169    
42170     onRender : function(ct, position)
42171     {
42172         if(!this.el){
42173             this.defaultAutoCreate = {
42174                 tag: "textarea",
42175                 style:"width:300px;height:60px;",
42176                 autocomplete: "off"
42177             };
42178         }
42179         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42180         /*
42181         if(this.grow){
42182             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42183             if(this.preventScrollbars){
42184                 this.el.setStyle("overflow", "hidden");
42185             }
42186             this.el.setHeight(this.growMin);
42187         }
42188         */
42189         //console.log('onrender' + this.getId() );
42190         Roo.form.FCKeditor.editors[this.getId()] = this;
42191          
42192
42193         this.replaceTextarea() ;
42194         
42195     },
42196     
42197     getEditor : function() {
42198         return this.fckEditor;
42199     },
42200     /**
42201      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42202      * @param {Mixed} value The value to set
42203      */
42204     
42205     
42206     setValue : function(value)
42207     {
42208         //console.log('setValue: ' + value);
42209         
42210         if(typeof(value) == 'undefined') { // not sure why this is happending...
42211             return;
42212         }
42213         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42214         
42215         //if(!this.el || !this.getEditor()) {
42216         //    this.value = value;
42217             //this.setValue.defer(100,this,[value]);    
42218         //    return;
42219         //} 
42220         
42221         if(!this.getEditor()) {
42222             return;
42223         }
42224         
42225         this.getEditor().SetData(value);
42226         
42227         //
42228
42229     },
42230
42231     /**
42232      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42233      * @return {Mixed} value The field value
42234      */
42235     getValue : function()
42236     {
42237         
42238         if (this.frame && this.frame.dom.style.display == 'none') {
42239             return Roo.form.FCKeditor.superclass.getValue.call(this);
42240         }
42241         
42242         if(!this.el || !this.getEditor()) {
42243            
42244            // this.getValue.defer(100,this); 
42245             return this.value;
42246         }
42247        
42248         
42249         var value=this.getEditor().GetData();
42250         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42251         return Roo.form.FCKeditor.superclass.getValue.call(this);
42252         
42253
42254     },
42255
42256     /**
42257      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42258      * @return {Mixed} value The field value
42259      */
42260     getRawValue : function()
42261     {
42262         if (this.frame && this.frame.dom.style.display == 'none') {
42263             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42264         }
42265         
42266         if(!this.el || !this.getEditor()) {
42267             //this.getRawValue.defer(100,this); 
42268             return this.value;
42269             return;
42270         }
42271         
42272         
42273         
42274         var value=this.getEditor().GetData();
42275         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42276         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42277          
42278     },
42279     
42280     setSize : function(w,h) {
42281         
42282         
42283         
42284         //if (this.frame && this.frame.dom.style.display == 'none') {
42285         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42286         //    return;
42287         //}
42288         //if(!this.el || !this.getEditor()) {
42289         //    this.setSize.defer(100,this, [w,h]); 
42290         //    return;
42291         //}
42292         
42293         
42294         
42295         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42296         
42297         this.frame.dom.setAttribute('width', w);
42298         this.frame.dom.setAttribute('height', h);
42299         this.frame.setSize(w,h);
42300         
42301     },
42302     
42303     toggleSourceEdit : function(value) {
42304         
42305       
42306          
42307         this.el.dom.style.display = value ? '' : 'none';
42308         this.frame.dom.style.display = value ?  'none' : '';
42309         
42310     },
42311     
42312     
42313     focus: function(tag)
42314     {
42315         if (this.frame.dom.style.display == 'none') {
42316             return Roo.form.FCKeditor.superclass.focus.call(this);
42317         }
42318         if(!this.el || !this.getEditor()) {
42319             this.focus.defer(100,this, [tag]); 
42320             return;
42321         }
42322         
42323         
42324         
42325         
42326         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42327         this.getEditor().Focus();
42328         if (tgs.length) {
42329             if (!this.getEditor().Selection.GetSelection()) {
42330                 this.focus.defer(100,this, [tag]); 
42331                 return;
42332             }
42333             
42334             
42335             var r = this.getEditor().EditorDocument.createRange();
42336             r.setStart(tgs[0],0);
42337             r.setEnd(tgs[0],0);
42338             this.getEditor().Selection.GetSelection().removeAllRanges();
42339             this.getEditor().Selection.GetSelection().addRange(r);
42340             this.getEditor().Focus();
42341         }
42342         
42343     },
42344     
42345     
42346     
42347     replaceTextarea : function()
42348     {
42349         if ( document.getElementById( this.getId() + '___Frame' ) )
42350             return ;
42351         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42352         //{
42353             // We must check the elements firstly using the Id and then the name.
42354         var oTextarea = document.getElementById( this.getId() );
42355         
42356         var colElementsByName = document.getElementsByName( this.getId() ) ;
42357          
42358         oTextarea.style.display = 'none' ;
42359
42360         if ( oTextarea.tabIndex ) {            
42361             this.TabIndex = oTextarea.tabIndex ;
42362         }
42363         
42364         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42365         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42366         this.frame = Roo.get(this.getId() + '___Frame')
42367     },
42368     
42369     _getConfigHtml : function()
42370     {
42371         var sConfig = '' ;
42372
42373         for ( var o in this.fckconfig ) {
42374             sConfig += sConfig.length > 0  ? '&amp;' : '';
42375             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42376         }
42377
42378         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42379     },
42380     
42381     
42382     _getIFrameHtml : function()
42383     {
42384         var sFile = 'fckeditor.html' ;
42385         /* no idea what this is about..
42386         try
42387         {
42388             if ( (/fcksource=true/i).test( window.top.location.search ) )
42389                 sFile = 'fckeditor.original.html' ;
42390         }
42391         catch (e) { 
42392         */
42393
42394         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42395         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42396         
42397         
42398         var html = '<iframe id="' + this.getId() +
42399             '___Frame" src="' + sLink +
42400             '" width="' + this.width +
42401             '" height="' + this.height + '"' +
42402             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42403             ' frameborder="0" scrolling="no"></iframe>' ;
42404
42405         return html ;
42406     },
42407     
42408     _insertHtmlBefore : function( html, element )
42409     {
42410         if ( element.insertAdjacentHTML )       {
42411             // IE
42412             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42413         } else { // Gecko
42414             var oRange = document.createRange() ;
42415             oRange.setStartBefore( element ) ;
42416             var oFragment = oRange.createContextualFragment( html );
42417             element.parentNode.insertBefore( oFragment, element ) ;
42418         }
42419     }
42420     
42421     
42422   
42423     
42424     
42425     
42426     
42427
42428 });
42429
42430 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42431
42432 function FCKeditor_OnComplete(editorInstance){
42433     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42434     f.fckEditor = editorInstance;
42435     //console.log("loaded");
42436     f.fireEvent('editorinit', f, editorInstance);
42437
42438   
42439
42440  
42441
42442
42443
42444
42445
42446
42447
42448
42449
42450
42451
42452
42453
42454
42455
42456 //<script type="text/javascript">
42457 /**
42458  * @class Roo.form.GridField
42459  * @extends Roo.form.Field
42460  * Embed a grid (or editable grid into a form)
42461  * STATUS ALPHA
42462  * 
42463  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42464  * it needs 
42465  * xgrid.store = Roo.data.Store
42466  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42467  * xgrid.store.reader = Roo.data.JsonReader 
42468  * 
42469  * 
42470  * @constructor
42471  * Creates a new GridField
42472  * @param {Object} config Configuration options
42473  */
42474 Roo.form.GridField = function(config){
42475     Roo.form.GridField.superclass.constructor.call(this, config);
42476      
42477 };
42478
42479 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42480     /**
42481      * @cfg {Number} width  - used to restrict width of grid..
42482      */
42483     width : 100,
42484     /**
42485      * @cfg {Number} height - used to restrict height of grid..
42486      */
42487     height : 50,
42488      /**
42489      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42490          * 
42491          *}
42492      */
42493     xgrid : false, 
42494     /**
42495      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42496      * {tag: "input", type: "checkbox", autocomplete: "off"})
42497      */
42498    // defaultAutoCreate : { tag: 'div' },
42499     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42500     /**
42501      * @cfg {String} addTitle Text to include for adding a title.
42502      */
42503     addTitle : false,
42504     //
42505     onResize : function(){
42506         Roo.form.Field.superclass.onResize.apply(this, arguments);
42507     },
42508
42509     initEvents : function(){
42510         // Roo.form.Checkbox.superclass.initEvents.call(this);
42511         // has no events...
42512        
42513     },
42514
42515
42516     getResizeEl : function(){
42517         return this.wrap;
42518     },
42519
42520     getPositionEl : function(){
42521         return this.wrap;
42522     },
42523
42524     // private
42525     onRender : function(ct, position){
42526         
42527         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42528         var style = this.style;
42529         delete this.style;
42530         
42531         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42532         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42533         this.viewEl = this.wrap.createChild({ tag: 'div' });
42534         if (style) {
42535             this.viewEl.applyStyles(style);
42536         }
42537         if (this.width) {
42538             this.viewEl.setWidth(this.width);
42539         }
42540         if (this.height) {
42541             this.viewEl.setHeight(this.height);
42542         }
42543         //if(this.inputValue !== undefined){
42544         //this.setValue(this.value);
42545         
42546         
42547         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42548         
42549         
42550         this.grid.render();
42551         this.grid.getDataSource().on('remove', this.refreshValue, this);
42552         this.grid.getDataSource().on('update', this.refreshValue, this);
42553         this.grid.on('afteredit', this.refreshValue, this);
42554  
42555     },
42556      
42557     
42558     /**
42559      * Sets the value of the item. 
42560      * @param {String} either an object  or a string..
42561      */
42562     setValue : function(v){
42563         //this.value = v;
42564         v = v || []; // empty set..
42565         // this does not seem smart - it really only affects memoryproxy grids..
42566         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42567             var ds = this.grid.getDataSource();
42568             // assumes a json reader..
42569             var data = {}
42570             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42571             ds.loadData( data);
42572         }
42573         Roo.form.GridField.superclass.setValue.call(this, v);
42574         this.refreshValue();
42575         // should load data in the grid really....
42576     },
42577     
42578     // private
42579     refreshValue: function() {
42580          var val = [];
42581         this.grid.getDataSource().each(function(r) {
42582             val.push(r.data);
42583         });
42584         this.el.dom.value = Roo.encode(val);
42585     }
42586     
42587      
42588     
42589     
42590 });/*
42591  * Based on:
42592  * Ext JS Library 1.1.1
42593  * Copyright(c) 2006-2007, Ext JS, LLC.
42594  *
42595  * Originally Released Under LGPL - original licence link has changed is not relivant.
42596  *
42597  * Fork - LGPL
42598  * <script type="text/javascript">
42599  */
42600 /**
42601  * @class Roo.form.DisplayField
42602  * @extends Roo.form.Field
42603  * A generic Field to display non-editable data.
42604  * @constructor
42605  * Creates a new Display Field item.
42606  * @param {Object} config Configuration options
42607  */
42608 Roo.form.DisplayField = function(config){
42609     Roo.form.DisplayField.superclass.constructor.call(this, config);
42610     
42611 };
42612
42613 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42614     inputType:      'hidden',
42615     allowBlank:     true,
42616     readOnly:         true,
42617     
42618  
42619     /**
42620      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42621      */
42622     focusClass : undefined,
42623     /**
42624      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42625      */
42626     fieldClass: 'x-form-field',
42627     
42628      /**
42629      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42630      */
42631     valueRenderer: undefined,
42632     
42633     width: 100,
42634     /**
42635      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42636      * {tag: "input", type: "checkbox", autocomplete: "off"})
42637      */
42638      
42639  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42640
42641     onResize : function(){
42642         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42643         
42644     },
42645
42646     initEvents : function(){
42647         // Roo.form.Checkbox.superclass.initEvents.call(this);
42648         // has no events...
42649        
42650     },
42651
42652
42653     getResizeEl : function(){
42654         return this.wrap;
42655     },
42656
42657     getPositionEl : function(){
42658         return this.wrap;
42659     },
42660
42661     // private
42662     onRender : function(ct, position){
42663         
42664         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42665         //if(this.inputValue !== undefined){
42666         this.wrap = this.el.wrap();
42667         
42668         this.viewEl = this.wrap.createChild({ tag: 'div'});
42669         
42670         if (this.bodyStyle) {
42671             this.viewEl.applyStyles(this.bodyStyle);
42672         }
42673         //this.viewEl.setStyle('padding', '2px');
42674         
42675         this.setValue(this.value);
42676         
42677     },
42678 /*
42679     // private
42680     initValue : Roo.emptyFn,
42681
42682   */
42683
42684         // private
42685     onClick : function(){
42686         
42687     },
42688
42689     /**
42690      * Sets the checked state of the checkbox.
42691      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42692      */
42693     setValue : function(v){
42694         this.value = v;
42695         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42696         // this might be called before we have a dom element..
42697         if (!this.viewEl) {
42698             return;
42699         }
42700         this.viewEl.dom.innerHTML = html;
42701         Roo.form.DisplayField.superclass.setValue.call(this, v);
42702
42703     }
42704 });//<script type="text/javasscript">
42705  
42706
42707 /**
42708  * @class Roo.DDView
42709  * A DnD enabled version of Roo.View.
42710  * @param {Element/String} container The Element in which to create the View.
42711  * @param {String} tpl The template string used to create the markup for each element of the View
42712  * @param {Object} config The configuration properties. These include all the config options of
42713  * {@link Roo.View} plus some specific to this class.<br>
42714  * <p>
42715  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42716  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42717  * <p>
42718  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42719 .x-view-drag-insert-above {
42720         border-top:1px dotted #3366cc;
42721 }
42722 .x-view-drag-insert-below {
42723         border-bottom:1px dotted #3366cc;
42724 }
42725 </code></pre>
42726  * 
42727  */
42728  
42729 Roo.DDView = function(container, tpl, config) {
42730     Roo.DDView.superclass.constructor.apply(this, arguments);
42731     this.getEl().setStyle("outline", "0px none");
42732     this.getEl().unselectable();
42733     if (this.dragGroup) {
42734                 this.setDraggable(this.dragGroup.split(","));
42735     }
42736     if (this.dropGroup) {
42737                 this.setDroppable(this.dropGroup.split(","));
42738     }
42739     if (this.deletable) {
42740         this.setDeletable();
42741     }
42742     this.isDirtyFlag = false;
42743         this.addEvents({
42744                 "drop" : true
42745         });
42746 };
42747
42748 Roo.extend(Roo.DDView, Roo.View, {
42749 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42750 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42751 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42752 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
42753
42754         isFormField: true,
42755
42756         reset: Roo.emptyFn,
42757         
42758         clearInvalid: Roo.form.Field.prototype.clearInvalid,
42759
42760         validate: function() {
42761                 return true;
42762         },
42763         
42764         destroy: function() {
42765                 this.purgeListeners();
42766                 this.getEl.removeAllListeners();
42767                 this.getEl().remove();
42768                 if (this.dragZone) {
42769                         if (this.dragZone.destroy) {
42770                                 this.dragZone.destroy();
42771                         }
42772                 }
42773                 if (this.dropZone) {
42774                         if (this.dropZone.destroy) {
42775                                 this.dropZone.destroy();
42776                         }
42777                 }
42778         },
42779
42780 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
42781         getName: function() {
42782                 return this.name;
42783         },
42784
42785 /**     Loads the View from a JSON string representing the Records to put into the Store. */
42786         setValue: function(v) {
42787                 if (!this.store) {
42788                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
42789                 }
42790                 var data = {};
42791                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
42792                 this.store.proxy = new Roo.data.MemoryProxy(data);
42793                 this.store.load();
42794         },
42795
42796 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
42797         getValue: function() {
42798                 var result = '(';
42799                 this.store.each(function(rec) {
42800                         result += rec.id + ',';
42801                 });
42802                 return result.substr(0, result.length - 1) + ')';
42803         },
42804         
42805         getIds: function() {
42806                 var i = 0, result = new Array(this.store.getCount());
42807                 this.store.each(function(rec) {
42808                         result[i++] = rec.id;
42809                 });
42810                 return result;
42811         },
42812         
42813         isDirty: function() {
42814                 return this.isDirtyFlag;
42815         },
42816
42817 /**
42818  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
42819  *      whole Element becomes the target, and this causes the drop gesture to append.
42820  */
42821     getTargetFromEvent : function(e) {
42822                 var target = e.getTarget();
42823                 while ((target !== null) && (target.parentNode != this.el.dom)) {
42824                 target = target.parentNode;
42825                 }
42826                 if (!target) {
42827                         target = this.el.dom.lastChild || this.el.dom;
42828                 }
42829                 return target;
42830     },
42831
42832 /**
42833  *      Create the drag data which consists of an object which has the property "ddel" as
42834  *      the drag proxy element. 
42835  */
42836     getDragData : function(e) {
42837         var target = this.findItemFromChild(e.getTarget());
42838                 if(target) {
42839                         this.handleSelection(e);
42840                         var selNodes = this.getSelectedNodes();
42841             var dragData = {
42842                 source: this,
42843                 copy: this.copy || (this.allowCopy && e.ctrlKey),
42844                 nodes: selNodes,
42845                 records: []
42846                         };
42847                         var selectedIndices = this.getSelectedIndexes();
42848                         for (var i = 0; i < selectedIndices.length; i++) {
42849                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
42850                         }
42851                         if (selNodes.length == 1) {
42852                                 dragData.ddel = target.cloneNode(true); // the div element
42853                         } else {
42854                                 var div = document.createElement('div'); // create the multi element drag "ghost"
42855                                 div.className = 'multi-proxy';
42856                                 for (var i = 0, len = selNodes.length; i < len; i++) {
42857                                         div.appendChild(selNodes[i].cloneNode(true));
42858                                 }
42859                                 dragData.ddel = div;
42860                         }
42861             //console.log(dragData)
42862             //console.log(dragData.ddel.innerHTML)
42863                         return dragData;
42864                 }
42865         //console.log('nodragData')
42866                 return false;
42867     },
42868     
42869 /**     Specify to which ddGroup items in this DDView may be dragged. */
42870     setDraggable: function(ddGroup) {
42871         if (ddGroup instanceof Array) {
42872                 Roo.each(ddGroup, this.setDraggable, this);
42873                 return;
42874         }
42875         if (this.dragZone) {
42876                 this.dragZone.addToGroup(ddGroup);
42877         } else {
42878                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
42879                                 containerScroll: true,
42880                                 ddGroup: ddGroup 
42881
42882                         });
42883 //                      Draggability implies selection. DragZone's mousedown selects the element.
42884                         if (!this.multiSelect) { this.singleSelect = true; }
42885
42886 //                      Wire the DragZone's handlers up to methods in *this*
42887                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
42888                 }
42889     },
42890
42891 /**     Specify from which ddGroup this DDView accepts drops. */
42892     setDroppable: function(ddGroup) {
42893         if (ddGroup instanceof Array) {
42894                 Roo.each(ddGroup, this.setDroppable, this);
42895                 return;
42896         }
42897         if (this.dropZone) {
42898                 this.dropZone.addToGroup(ddGroup);
42899         } else {
42900                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
42901                                 containerScroll: true,
42902                                 ddGroup: ddGroup
42903                         });
42904
42905 //                      Wire the DropZone's handlers up to methods in *this*
42906                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
42907                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
42908                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
42909                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
42910                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
42911                 }
42912     },
42913
42914 /**     Decide whether to drop above or below a View node. */
42915     getDropPoint : function(e, n, dd){
42916         if (n == this.el.dom) { return "above"; }
42917                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
42918                 var c = t + (b - t) / 2;
42919                 var y = Roo.lib.Event.getPageY(e);
42920                 if(y <= c) {
42921                         return "above";
42922                 }else{
42923                         return "below";
42924                 }
42925     },
42926
42927     onNodeEnter : function(n, dd, e, data){
42928                 return false;
42929     },
42930     
42931     onNodeOver : function(n, dd, e, data){
42932                 var pt = this.getDropPoint(e, n, dd);
42933                 // set the insert point style on the target node
42934                 var dragElClass = this.dropNotAllowed;
42935                 if (pt) {
42936                         var targetElClass;
42937                         if (pt == "above"){
42938                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
42939                                 targetElClass = "x-view-drag-insert-above";
42940                         } else {
42941                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
42942                                 targetElClass = "x-view-drag-insert-below";
42943                         }
42944                         if (this.lastInsertClass != targetElClass){
42945                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
42946                                 this.lastInsertClass = targetElClass;
42947                         }
42948                 }
42949                 return dragElClass;
42950         },
42951
42952     onNodeOut : function(n, dd, e, data){
42953                 this.removeDropIndicators(n);
42954     },
42955
42956     onNodeDrop : function(n, dd, e, data){
42957         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
42958                 return false;
42959         }
42960         var pt = this.getDropPoint(e, n, dd);
42961                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
42962                 if (pt == "below") { insertAt++; }
42963                 for (var i = 0; i < data.records.length; i++) {
42964                         var r = data.records[i];
42965                         var dup = this.store.getById(r.id);
42966                         if (dup && (dd != this.dragZone)) {
42967                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
42968                         } else {
42969                                 if (data.copy) {
42970                                         this.store.insert(insertAt++, r.copy());
42971                                 } else {
42972                                         data.source.isDirtyFlag = true;
42973                                         r.store.remove(r);
42974                                         this.store.insert(insertAt++, r);
42975                                 }
42976                                 this.isDirtyFlag = true;
42977                         }
42978                 }
42979                 this.dragZone.cachedTarget = null;
42980                 return true;
42981     },
42982
42983     removeDropIndicators : function(n){
42984                 if(n){
42985                         Roo.fly(n).removeClass([
42986                                 "x-view-drag-insert-above",
42987                                 "x-view-drag-insert-below"]);
42988                         this.lastInsertClass = "_noclass";
42989                 }
42990     },
42991
42992 /**
42993  *      Utility method. Add a delete option to the DDView's context menu.
42994  *      @param {String} imageUrl The URL of the "delete" icon image.
42995  */
42996         setDeletable: function(imageUrl) {
42997                 if (!this.singleSelect && !this.multiSelect) {
42998                         this.singleSelect = true;
42999                 }
43000                 var c = this.getContextMenu();
43001                 this.contextMenu.on("itemclick", function(item) {
43002                         switch (item.id) {
43003                                 case "delete":
43004                                         this.remove(this.getSelectedIndexes());
43005                                         break;
43006                         }
43007                 }, this);
43008                 this.contextMenu.add({
43009                         icon: imageUrl,
43010                         id: "delete",
43011                         text: 'Delete'
43012                 });
43013         },
43014         
43015 /**     Return the context menu for this DDView. */
43016         getContextMenu: function() {
43017                 if (!this.contextMenu) {
43018 //                      Create the View's context menu
43019                         this.contextMenu = new Roo.menu.Menu({
43020                                 id: this.id + "-contextmenu"
43021                         });
43022                         this.el.on("contextmenu", this.showContextMenu, this);
43023                 }
43024                 return this.contextMenu;
43025         },
43026         
43027         disableContextMenu: function() {
43028                 if (this.contextMenu) {
43029                         this.el.un("contextmenu", this.showContextMenu, this);
43030                 }
43031         },
43032
43033         showContextMenu: function(e, item) {
43034         item = this.findItemFromChild(e.getTarget());
43035                 if (item) {
43036                         e.stopEvent();
43037                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
43038                         this.contextMenu.showAt(e.getXY());
43039             }
43040     },
43041
43042 /**
43043  *      Remove {@link Roo.data.Record}s at the specified indices.
43044  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
43045  */
43046     remove: function(selectedIndices) {
43047                 selectedIndices = [].concat(selectedIndices);
43048                 for (var i = 0; i < selectedIndices.length; i++) {
43049                         var rec = this.store.getAt(selectedIndices[i]);
43050                         this.store.remove(rec);
43051                 }
43052     },
43053
43054 /**
43055  *      Double click fires the event, but also, if this is draggable, and there is only one other
43056  *      related DropZone, it transfers the selected node.
43057  */
43058     onDblClick : function(e){
43059         var item = this.findItemFromChild(e.getTarget());
43060         if(item){
43061             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
43062                 return false;
43063             }
43064             if (this.dragGroup) {
43065                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
43066                     while (targets.indexOf(this.dropZone) > -1) {
43067                             targets.remove(this.dropZone);
43068                                 }
43069                     if (targets.length == 1) {
43070                                         this.dragZone.cachedTarget = null;
43071                         var el = Roo.get(targets[0].getEl());
43072                         var box = el.getBox(true);
43073                         targets[0].onNodeDrop(el.dom, {
43074                                 target: el.dom,
43075                                 xy: [box.x, box.y + box.height - 1]
43076                         }, null, this.getDragData(e));
43077                     }
43078                 }
43079         }
43080     },
43081     
43082     handleSelection: function(e) {
43083                 this.dragZone.cachedTarget = null;
43084         var item = this.findItemFromChild(e.getTarget());
43085         if (!item) {
43086                 this.clearSelections(true);
43087                 return;
43088         }
43089                 if (item && (this.multiSelect || this.singleSelect)){
43090                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
43091                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
43092                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
43093                                 this.unselect(item);
43094                         } else {
43095                                 this.select(item, this.multiSelect && e.ctrlKey);
43096                                 this.lastSelection = item;
43097                         }
43098                 }
43099     },
43100
43101     onItemClick : function(item, index, e){
43102                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
43103                         return false;
43104                 }
43105                 return true;
43106     },
43107
43108     unselect : function(nodeInfo, suppressEvent){
43109                 var node = this.getNode(nodeInfo);
43110                 if(node && this.isSelected(node)){
43111                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
43112                                 Roo.fly(node).removeClass(this.selectedClass);
43113                                 this.selections.remove(node);
43114                                 if(!suppressEvent){
43115                                         this.fireEvent("selectionchange", this, this.selections);
43116                                 }
43117                         }
43118                 }
43119     }
43120 });
43121 /*
43122  * Based on:
43123  * Ext JS Library 1.1.1
43124  * Copyright(c) 2006-2007, Ext JS, LLC.
43125  *
43126  * Originally Released Under LGPL - original licence link has changed is not relivant.
43127  *
43128  * Fork - LGPL
43129  * <script type="text/javascript">
43130  */
43131  
43132 /**
43133  * @class Roo.LayoutManager
43134  * @extends Roo.util.Observable
43135  * Base class for layout managers.
43136  */
43137 Roo.LayoutManager = function(container, config){
43138     Roo.LayoutManager.superclass.constructor.call(this);
43139     this.el = Roo.get(container);
43140     // ie scrollbar fix
43141     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43142         document.body.scroll = "no";
43143     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43144         this.el.position('relative');
43145     }
43146     this.id = this.el.id;
43147     this.el.addClass("x-layout-container");
43148     /** false to disable window resize monitoring @type Boolean */
43149     this.monitorWindowResize = true;
43150     this.regions = {};
43151     this.addEvents({
43152         /**
43153          * @event layout
43154          * Fires when a layout is performed. 
43155          * @param {Roo.LayoutManager} this
43156          */
43157         "layout" : true,
43158         /**
43159          * @event regionresized
43160          * Fires when the user resizes a region. 
43161          * @param {Roo.LayoutRegion} region The resized region
43162          * @param {Number} newSize The new size (width for east/west, height for north/south)
43163          */
43164         "regionresized" : true,
43165         /**
43166          * @event regioncollapsed
43167          * Fires when a region is collapsed. 
43168          * @param {Roo.LayoutRegion} region The collapsed region
43169          */
43170         "regioncollapsed" : true,
43171         /**
43172          * @event regionexpanded
43173          * Fires when a region is expanded.  
43174          * @param {Roo.LayoutRegion} region The expanded region
43175          */
43176         "regionexpanded" : true
43177     });
43178     this.updating = false;
43179     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43180 };
43181
43182 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
43183     /**
43184      * Returns true if this layout is currently being updated
43185      * @return {Boolean}
43186      */
43187     isUpdating : function(){
43188         return this.updating; 
43189     },
43190     
43191     /**
43192      * Suspend the LayoutManager from doing auto-layouts while
43193      * making multiple add or remove calls
43194      */
43195     beginUpdate : function(){
43196         this.updating = true;    
43197     },
43198     
43199     /**
43200      * Restore auto-layouts and optionally disable the manager from performing a layout
43201      * @param {Boolean} noLayout true to disable a layout update 
43202      */
43203     endUpdate : function(noLayout){
43204         this.updating = false;
43205         if(!noLayout){
43206             this.layout();
43207         }    
43208     },
43209     
43210     layout: function(){
43211         
43212     },
43213     
43214     onRegionResized : function(region, newSize){
43215         this.fireEvent("regionresized", region, newSize);
43216         this.layout();
43217     },
43218     
43219     onRegionCollapsed : function(region){
43220         this.fireEvent("regioncollapsed", region);
43221     },
43222     
43223     onRegionExpanded : function(region){
43224         this.fireEvent("regionexpanded", region);
43225     },
43226         
43227     /**
43228      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43229      * performs box-model adjustments.
43230      * @return {Object} The size as an object {width: (the width), height: (the height)}
43231      */
43232     getViewSize : function(){
43233         var size;
43234         if(this.el.dom != document.body){
43235             size = this.el.getSize();
43236         }else{
43237             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43238         }
43239         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43240         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43241         return size;
43242     },
43243     
43244     /**
43245      * Returns the Element this layout is bound to.
43246      * @return {Roo.Element}
43247      */
43248     getEl : function(){
43249         return this.el;
43250     },
43251     
43252     /**
43253      * Returns the specified region.
43254      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43255      * @return {Roo.LayoutRegion}
43256      */
43257     getRegion : function(target){
43258         return this.regions[target.toLowerCase()];
43259     },
43260     
43261     onWindowResize : function(){
43262         if(this.monitorWindowResize){
43263             this.layout();
43264         }
43265     }
43266 });/*
43267  * Based on:
43268  * Ext JS Library 1.1.1
43269  * Copyright(c) 2006-2007, Ext JS, LLC.
43270  *
43271  * Originally Released Under LGPL - original licence link has changed is not relivant.
43272  *
43273  * Fork - LGPL
43274  * <script type="text/javascript">
43275  */
43276 /**
43277  * @class Roo.BorderLayout
43278  * @extends Roo.LayoutManager
43279  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43280  * please see: <br><br>
43281  * <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>
43282  * <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>
43283  * Example:
43284  <pre><code>
43285  var layout = new Roo.BorderLayout(document.body, {
43286     north: {
43287         initialSize: 25,
43288         titlebar: false
43289     },
43290     west: {
43291         split:true,
43292         initialSize: 200,
43293         minSize: 175,
43294         maxSize: 400,
43295         titlebar: true,
43296         collapsible: true
43297     },
43298     east: {
43299         split:true,
43300         initialSize: 202,
43301         minSize: 175,
43302         maxSize: 400,
43303         titlebar: true,
43304         collapsible: true
43305     },
43306     south: {
43307         split:true,
43308         initialSize: 100,
43309         minSize: 100,
43310         maxSize: 200,
43311         titlebar: true,
43312         collapsible: true
43313     },
43314     center: {
43315         titlebar: true,
43316         autoScroll:true,
43317         resizeTabs: true,
43318         minTabWidth: 50,
43319         preferredTabWidth: 150
43320     }
43321 });
43322
43323 // shorthand
43324 var CP = Roo.ContentPanel;
43325
43326 layout.beginUpdate();
43327 layout.add("north", new CP("north", "North"));
43328 layout.add("south", new CP("south", {title: "South", closable: true}));
43329 layout.add("west", new CP("west", {title: "West"}));
43330 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43331 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43332 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43333 layout.getRegion("center").showPanel("center1");
43334 layout.endUpdate();
43335 </code></pre>
43336
43337 <b>The container the layout is rendered into can be either the body element or any other element.
43338 If it is not the body element, the container needs to either be an absolute positioned element,
43339 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43340 the container size if it is not the body element.</b>
43341
43342 * @constructor
43343 * Create a new BorderLayout
43344 * @param {String/HTMLElement/Element} container The container this layout is bound to
43345 * @param {Object} config Configuration options
43346  */
43347 Roo.BorderLayout = function(container, config){
43348     config = config || {};
43349     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43350     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43351     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43352         var target = this.factory.validRegions[i];
43353         if(config[target]){
43354             this.addRegion(target, config[target]);
43355         }
43356     }
43357 };
43358
43359 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43360     /**
43361      * Creates and adds a new region if it doesn't already exist.
43362      * @param {String} target The target region key (north, south, east, west or center).
43363      * @param {Object} config The regions config object
43364      * @return {BorderLayoutRegion} The new region
43365      */
43366     addRegion : function(target, config){
43367         if(!this.regions[target]){
43368             var r = this.factory.create(target, this, config);
43369             this.bindRegion(target, r);
43370         }
43371         return this.regions[target];
43372     },
43373
43374     // private (kinda)
43375     bindRegion : function(name, r){
43376         this.regions[name] = r;
43377         r.on("visibilitychange", this.layout, this);
43378         r.on("paneladded", this.layout, this);
43379         r.on("panelremoved", this.layout, this);
43380         r.on("invalidated", this.layout, this);
43381         r.on("resized", this.onRegionResized, this);
43382         r.on("collapsed", this.onRegionCollapsed, this);
43383         r.on("expanded", this.onRegionExpanded, this);
43384     },
43385
43386     /**
43387      * Performs a layout update.
43388      */
43389     layout : function(){
43390         if(this.updating) return;
43391         var size = this.getViewSize();
43392         var w = size.width;
43393         var h = size.height;
43394         var centerW = w;
43395         var centerH = h;
43396         var centerY = 0;
43397         var centerX = 0;
43398         //var x = 0, y = 0;
43399
43400         var rs = this.regions;
43401         var north = rs["north"];
43402         var south = rs["south"]; 
43403         var west = rs["west"];
43404         var east = rs["east"];
43405         var center = rs["center"];
43406         //if(this.hideOnLayout){ // not supported anymore
43407             //c.el.setStyle("display", "none");
43408         //}
43409         if(north && north.isVisible()){
43410             var b = north.getBox();
43411             var m = north.getMargins();
43412             b.width = w - (m.left+m.right);
43413             b.x = m.left;
43414             b.y = m.top;
43415             centerY = b.height + b.y + m.bottom;
43416             centerH -= centerY;
43417             north.updateBox(this.safeBox(b));
43418         }
43419         if(south && south.isVisible()){
43420             var b = south.getBox();
43421             var m = south.getMargins();
43422             b.width = w - (m.left+m.right);
43423             b.x = m.left;
43424             var totalHeight = (b.height + m.top + m.bottom);
43425             b.y = h - totalHeight + m.top;
43426             centerH -= totalHeight;
43427             south.updateBox(this.safeBox(b));
43428         }
43429         if(west && west.isVisible()){
43430             var b = west.getBox();
43431             var m = west.getMargins();
43432             b.height = centerH - (m.top+m.bottom);
43433             b.x = m.left;
43434             b.y = centerY + m.top;
43435             var totalWidth = (b.width + m.left + m.right);
43436             centerX += totalWidth;
43437             centerW -= totalWidth;
43438             west.updateBox(this.safeBox(b));
43439         }
43440         if(east && east.isVisible()){
43441             var b = east.getBox();
43442             var m = east.getMargins();
43443             b.height = centerH - (m.top+m.bottom);
43444             var totalWidth = (b.width + m.left + m.right);
43445             b.x = w - totalWidth + m.left;
43446             b.y = centerY + m.top;
43447             centerW -= totalWidth;
43448             east.updateBox(this.safeBox(b));
43449         }
43450         if(center){
43451             var m = center.getMargins();
43452             var centerBox = {
43453                 x: centerX + m.left,
43454                 y: centerY + m.top,
43455                 width: centerW - (m.left+m.right),
43456                 height: centerH - (m.top+m.bottom)
43457             };
43458             //if(this.hideOnLayout){
43459                 //center.el.setStyle("display", "block");
43460             //}
43461             center.updateBox(this.safeBox(centerBox));
43462         }
43463         this.el.repaint();
43464         this.fireEvent("layout", this);
43465     },
43466
43467     // private
43468     safeBox : function(box){
43469         box.width = Math.max(0, box.width);
43470         box.height = Math.max(0, box.height);
43471         return box;
43472     },
43473
43474     /**
43475      * Adds a ContentPanel (or subclass) to this layout.
43476      * @param {String} target The target region key (north, south, east, west or center).
43477      * @param {Roo.ContentPanel} panel The panel to add
43478      * @return {Roo.ContentPanel} The added panel
43479      */
43480     add : function(target, panel){
43481          
43482         target = target.toLowerCase();
43483         return this.regions[target].add(panel);
43484     },
43485
43486     /**
43487      * Remove a ContentPanel (or subclass) to this layout.
43488      * @param {String} target The target region key (north, south, east, west or center).
43489      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43490      * @return {Roo.ContentPanel} The removed panel
43491      */
43492     remove : function(target, panel){
43493         target = target.toLowerCase();
43494         return this.regions[target].remove(panel);
43495     },
43496
43497     /**
43498      * Searches all regions for a panel with the specified id
43499      * @param {String} panelId
43500      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43501      */
43502     findPanel : function(panelId){
43503         var rs = this.regions;
43504         for(var target in rs){
43505             if(typeof rs[target] != "function"){
43506                 var p = rs[target].getPanel(panelId);
43507                 if(p){
43508                     return p;
43509                 }
43510             }
43511         }
43512         return null;
43513     },
43514
43515     /**
43516      * Searches all regions for a panel with the specified id and activates (shows) it.
43517      * @param {String/ContentPanel} panelId The panels id or the panel itself
43518      * @return {Roo.ContentPanel} The shown panel or null
43519      */
43520     showPanel : function(panelId) {
43521       var rs = this.regions;
43522       for(var target in rs){
43523          var r = rs[target];
43524          if(typeof r != "function"){
43525             if(r.hasPanel(panelId)){
43526                return r.showPanel(panelId);
43527             }
43528          }
43529       }
43530       return null;
43531    },
43532
43533    /**
43534      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43535      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43536      */
43537     restoreState : function(provider){
43538         if(!provider){
43539             provider = Roo.state.Manager;
43540         }
43541         var sm = new Roo.LayoutStateManager();
43542         sm.init(this, provider);
43543     },
43544
43545     /**
43546      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43547      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43548      * a valid ContentPanel config object.  Example:
43549      * <pre><code>
43550 // Create the main layout
43551 var layout = new Roo.BorderLayout('main-ct', {
43552     west: {
43553         split:true,
43554         minSize: 175,
43555         titlebar: true
43556     },
43557     center: {
43558         title:'Components'
43559     }
43560 }, 'main-ct');
43561
43562 // Create and add multiple ContentPanels at once via configs
43563 layout.batchAdd({
43564    west: {
43565        id: 'source-files',
43566        autoCreate:true,
43567        title:'Ext Source Files',
43568        autoScroll:true,
43569        fitToFrame:true
43570    },
43571    center : {
43572        el: cview,
43573        autoScroll:true,
43574        fitToFrame:true,
43575        toolbar: tb,
43576        resizeEl:'cbody'
43577    }
43578 });
43579 </code></pre>
43580      * @param {Object} regions An object containing ContentPanel configs by region name
43581      */
43582     batchAdd : function(regions){
43583         this.beginUpdate();
43584         for(var rname in regions){
43585             var lr = this.regions[rname];
43586             if(lr){
43587                 this.addTypedPanels(lr, regions[rname]);
43588             }
43589         }
43590         this.endUpdate();
43591     },
43592
43593     // private
43594     addTypedPanels : function(lr, ps){
43595         if(typeof ps == 'string'){
43596             lr.add(new Roo.ContentPanel(ps));
43597         }
43598         else if(ps instanceof Array){
43599             for(var i =0, len = ps.length; i < len; i++){
43600                 this.addTypedPanels(lr, ps[i]);
43601             }
43602         }
43603         else if(!ps.events){ // raw config?
43604             var el = ps.el;
43605             delete ps.el; // prevent conflict
43606             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43607         }
43608         else {  // panel object assumed!
43609             lr.add(ps);
43610         }
43611     },
43612     /**
43613      * Adds a xtype elements to the layout.
43614      * <pre><code>
43615
43616 layout.addxtype({
43617        xtype : 'ContentPanel',
43618        region: 'west',
43619        items: [ .... ]
43620    }
43621 );
43622
43623 layout.addxtype({
43624         xtype : 'NestedLayoutPanel',
43625         region: 'west',
43626         layout: {
43627            center: { },
43628            west: { }   
43629         },
43630         items : [ ... list of content panels or nested layout panels.. ]
43631    }
43632 );
43633 </code></pre>
43634      * @param {Object} cfg Xtype definition of item to add.
43635      */
43636     addxtype : function(cfg)
43637     {
43638         // basically accepts a pannel...
43639         // can accept a layout region..!?!?
43640        // console.log('BorderLayout add ' + cfg.xtype)
43641         
43642         if (!cfg.xtype.match(/Panel$/)) {
43643             return false;
43644         }
43645         var ret = false;
43646         var region = cfg.region;
43647         delete cfg.region;
43648         
43649           
43650         var xitems = [];
43651         if (cfg.items) {
43652             xitems = cfg.items;
43653             delete cfg.items;
43654         }
43655         
43656         
43657         switch(cfg.xtype) 
43658         {
43659             case 'ContentPanel':  // ContentPanel (el, cfg)
43660             case 'ScrollPanel':  // ContentPanel (el, cfg)
43661                 if(cfg.autoCreate) {
43662                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43663                 } else {
43664                     var el = this.el.createChild();
43665                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43666                 }
43667                 
43668                 this.add(region, ret);
43669                 break;
43670             
43671             
43672             case 'TreePanel': // our new panel!
43673                 cfg.el = this.el.createChild();
43674                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43675                 this.add(region, ret);
43676                 break;
43677             
43678             case 'NestedLayoutPanel': 
43679                 // create a new Layout (which is  a Border Layout...
43680                 var el = this.el.createChild();
43681                 var clayout = cfg.layout;
43682                 delete cfg.layout;
43683                 clayout.items   = clayout.items  || [];
43684                 // replace this exitems with the clayout ones..
43685                 xitems = clayout.items;
43686                  
43687                 
43688                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43689                     cfg.background = false;
43690                 }
43691                 var layout = new Roo.BorderLayout(el, clayout);
43692                 
43693                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43694                 //console.log('adding nested layout panel '  + cfg.toSource());
43695                 this.add(region, ret);
43696                 
43697                 break;
43698                 
43699             case 'GridPanel': 
43700             
43701                 // needs grid and region
43702                 
43703                 //var el = this.getRegion(region).el.createChild();
43704                 var el = this.el.createChild();
43705                 // create the grid first...
43706                 
43707                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43708                 delete cfg.grid;
43709                 if (region == 'center' && this.active ) {
43710                     cfg.background = false;
43711                 }
43712                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43713                 
43714                 this.add(region, ret);
43715                 if (cfg.background) {
43716                     ret.on('activate', function(gp) {
43717                         if (!gp.grid.rendered) {
43718                             gp.grid.render();
43719                         }
43720                     });
43721                 } else {
43722                     grid.render();
43723                 }
43724                 break;
43725            
43726                
43727                 
43728                 
43729             default: 
43730                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43731                 return;
43732              // GridPanel (grid, cfg)
43733             
43734         }
43735         this.beginUpdate();
43736         // add children..
43737         Roo.each(xitems, function(i)  {
43738             ret.addxtype(i);
43739         });
43740         this.endUpdate();
43741         return ret;
43742         
43743     }
43744 });
43745
43746 /**
43747  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43748  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43749  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43750  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43751  * <pre><code>
43752 // shorthand
43753 var CP = Roo.ContentPanel;
43754
43755 var layout = Roo.BorderLayout.create({
43756     north: {
43757         initialSize: 25,
43758         titlebar: false,
43759         panels: [new CP("north", "North")]
43760     },
43761     west: {
43762         split:true,
43763         initialSize: 200,
43764         minSize: 175,
43765         maxSize: 400,
43766         titlebar: true,
43767         collapsible: true,
43768         panels: [new CP("west", {title: "West"})]
43769     },
43770     east: {
43771         split:true,
43772         initialSize: 202,
43773         minSize: 175,
43774         maxSize: 400,
43775         titlebar: true,
43776         collapsible: true,
43777         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
43778     },
43779     south: {
43780         split:true,
43781         initialSize: 100,
43782         minSize: 100,
43783         maxSize: 200,
43784         titlebar: true,
43785         collapsible: true,
43786         panels: [new CP("south", {title: "South", closable: true})]
43787     },
43788     center: {
43789         titlebar: true,
43790         autoScroll:true,
43791         resizeTabs: true,
43792         minTabWidth: 50,
43793         preferredTabWidth: 150,
43794         panels: [
43795             new CP("center1", {title: "Close Me", closable: true}),
43796             new CP("center2", {title: "Center Panel", closable: false})
43797         ]
43798     }
43799 }, document.body);
43800
43801 layout.getRegion("center").showPanel("center1");
43802 </code></pre>
43803  * @param config
43804  * @param targetEl
43805  */
43806 Roo.BorderLayout.create = function(config, targetEl){
43807     var layout = new Roo.BorderLayout(targetEl || document.body, config);
43808     layout.beginUpdate();
43809     var regions = Roo.BorderLayout.RegionFactory.validRegions;
43810     for(var j = 0, jlen = regions.length; j < jlen; j++){
43811         var lr = regions[j];
43812         if(layout.regions[lr] && config[lr].panels){
43813             var r = layout.regions[lr];
43814             var ps = config[lr].panels;
43815             layout.addTypedPanels(r, ps);
43816         }
43817     }
43818     layout.endUpdate();
43819     return layout;
43820 };
43821
43822 // private
43823 Roo.BorderLayout.RegionFactory = {
43824     // private
43825     validRegions : ["north","south","east","west","center"],
43826
43827     // private
43828     create : function(target, mgr, config){
43829         target = target.toLowerCase();
43830         if(config.lightweight || config.basic){
43831             return new Roo.BasicLayoutRegion(mgr, config, target);
43832         }
43833         switch(target){
43834             case "north":
43835                 return new Roo.NorthLayoutRegion(mgr, config);
43836             case "south":
43837                 return new Roo.SouthLayoutRegion(mgr, config);
43838             case "east":
43839                 return new Roo.EastLayoutRegion(mgr, config);
43840             case "west":
43841                 return new Roo.WestLayoutRegion(mgr, config);
43842             case "center":
43843                 return new Roo.CenterLayoutRegion(mgr, config);
43844         }
43845         throw 'Layout region "'+target+'" not supported.';
43846     }
43847 };/*
43848  * Based on:
43849  * Ext JS Library 1.1.1
43850  * Copyright(c) 2006-2007, Ext JS, LLC.
43851  *
43852  * Originally Released Under LGPL - original licence link has changed is not relivant.
43853  *
43854  * Fork - LGPL
43855  * <script type="text/javascript">
43856  */
43857  
43858 /**
43859  * @class Roo.BasicLayoutRegion
43860  * @extends Roo.util.Observable
43861  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43862  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43863  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43864  */
43865 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
43866     this.mgr = mgr;
43867     this.position  = pos;
43868     this.events = {
43869         /**
43870          * @scope Roo.BasicLayoutRegion
43871          */
43872         
43873         /**
43874          * @event beforeremove
43875          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43876          * @param {Roo.LayoutRegion} this
43877          * @param {Roo.ContentPanel} panel The panel
43878          * @param {Object} e The cancel event object
43879          */
43880         "beforeremove" : true,
43881         /**
43882          * @event invalidated
43883          * Fires when the layout for this region is changed.
43884          * @param {Roo.LayoutRegion} this
43885          */
43886         "invalidated" : true,
43887         /**
43888          * @event visibilitychange
43889          * Fires when this region is shown or hidden 
43890          * @param {Roo.LayoutRegion} this
43891          * @param {Boolean} visibility true or false
43892          */
43893         "visibilitychange" : true,
43894         /**
43895          * @event paneladded
43896          * Fires when a panel is added. 
43897          * @param {Roo.LayoutRegion} this
43898          * @param {Roo.ContentPanel} panel The panel
43899          */
43900         "paneladded" : true,
43901         /**
43902          * @event panelremoved
43903          * Fires when a panel is removed. 
43904          * @param {Roo.LayoutRegion} this
43905          * @param {Roo.ContentPanel} panel The panel
43906          */
43907         "panelremoved" : true,
43908         /**
43909          * @event collapsed
43910          * Fires when this region is collapsed.
43911          * @param {Roo.LayoutRegion} this
43912          */
43913         "collapsed" : true,
43914         /**
43915          * @event expanded
43916          * Fires when this region is expanded.
43917          * @param {Roo.LayoutRegion} this
43918          */
43919         "expanded" : true,
43920         /**
43921          * @event slideshow
43922          * Fires when this region is slid into view.
43923          * @param {Roo.LayoutRegion} this
43924          */
43925         "slideshow" : true,
43926         /**
43927          * @event slidehide
43928          * Fires when this region slides out of view. 
43929          * @param {Roo.LayoutRegion} this
43930          */
43931         "slidehide" : true,
43932         /**
43933          * @event panelactivated
43934          * Fires when a panel is activated. 
43935          * @param {Roo.LayoutRegion} this
43936          * @param {Roo.ContentPanel} panel The activated panel
43937          */
43938         "panelactivated" : true,
43939         /**
43940          * @event resized
43941          * Fires when the user resizes this region. 
43942          * @param {Roo.LayoutRegion} this
43943          * @param {Number} newSize The new size (width for east/west, height for north/south)
43944          */
43945         "resized" : true
43946     };
43947     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43948     this.panels = new Roo.util.MixedCollection();
43949     this.panels.getKey = this.getPanelId.createDelegate(this);
43950     this.box = null;
43951     this.activePanel = null;
43952     // ensure listeners are added...
43953     
43954     if (config.listeners || config.events) {
43955         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
43956             listeners : config.listeners || {},
43957             events : config.events || {}
43958         });
43959     }
43960     
43961     if(skipConfig !== true){
43962         this.applyConfig(config);
43963     }
43964 };
43965
43966 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
43967     getPanelId : function(p){
43968         return p.getId();
43969     },
43970     
43971     applyConfig : function(config){
43972         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43973         this.config = config;
43974         
43975     },
43976     
43977     /**
43978      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43979      * the width, for horizontal (north, south) the height.
43980      * @param {Number} newSize The new width or height
43981      */
43982     resizeTo : function(newSize){
43983         var el = this.el ? this.el :
43984                  (this.activePanel ? this.activePanel.getEl() : null);
43985         if(el){
43986             switch(this.position){
43987                 case "east":
43988                 case "west":
43989                     el.setWidth(newSize);
43990                     this.fireEvent("resized", this, newSize);
43991                 break;
43992                 case "north":
43993                 case "south":
43994                     el.setHeight(newSize);
43995                     this.fireEvent("resized", this, newSize);
43996                 break;                
43997             }
43998         }
43999     },
44000     
44001     getBox : function(){
44002         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
44003     },
44004     
44005     getMargins : function(){
44006         return this.margins;
44007     },
44008     
44009     updateBox : function(box){
44010         this.box = box;
44011         var el = this.activePanel.getEl();
44012         el.dom.style.left = box.x + "px";
44013         el.dom.style.top = box.y + "px";
44014         this.activePanel.setSize(box.width, box.height);
44015     },
44016     
44017     /**
44018      * Returns the container element for this region.
44019      * @return {Roo.Element}
44020      */
44021     getEl : function(){
44022         return this.activePanel;
44023     },
44024     
44025     /**
44026      * Returns true if this region is currently visible.
44027      * @return {Boolean}
44028      */
44029     isVisible : function(){
44030         return this.activePanel ? true : false;
44031     },
44032     
44033     setActivePanel : function(panel){
44034         panel = this.getPanel(panel);
44035         if(this.activePanel && this.activePanel != panel){
44036             this.activePanel.setActiveState(false);
44037             this.activePanel.getEl().setLeftTop(-10000,-10000);
44038         }
44039         this.activePanel = panel;
44040         panel.setActiveState(true);
44041         if(this.box){
44042             panel.setSize(this.box.width, this.box.height);
44043         }
44044         this.fireEvent("panelactivated", this, panel);
44045         this.fireEvent("invalidated");
44046     },
44047     
44048     /**
44049      * Show the specified panel.
44050      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
44051      * @return {Roo.ContentPanel} The shown panel or null
44052      */
44053     showPanel : function(panel){
44054         if(panel = this.getPanel(panel)){
44055             this.setActivePanel(panel);
44056         }
44057         return panel;
44058     },
44059     
44060     /**
44061      * Get the active panel for this region.
44062      * @return {Roo.ContentPanel} The active panel or null
44063      */
44064     getActivePanel : function(){
44065         return this.activePanel;
44066     },
44067     
44068     /**
44069      * Add the passed ContentPanel(s)
44070      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44071      * @return {Roo.ContentPanel} The panel added (if only one was added)
44072      */
44073     add : function(panel){
44074         if(arguments.length > 1){
44075             for(var i = 0, len = arguments.length; i < len; i++) {
44076                 this.add(arguments[i]);
44077             }
44078             return null;
44079         }
44080         if(this.hasPanel(panel)){
44081             this.showPanel(panel);
44082             return panel;
44083         }
44084         var el = panel.getEl();
44085         if(el.dom.parentNode != this.mgr.el.dom){
44086             this.mgr.el.dom.appendChild(el.dom);
44087         }
44088         if(panel.setRegion){
44089             panel.setRegion(this);
44090         }
44091         this.panels.add(panel);
44092         el.setStyle("position", "absolute");
44093         if(!panel.background){
44094             this.setActivePanel(panel);
44095             if(this.config.initialSize && this.panels.getCount()==1){
44096                 this.resizeTo(this.config.initialSize);
44097             }
44098         }
44099         this.fireEvent("paneladded", this, panel);
44100         return panel;
44101     },
44102     
44103     /**
44104      * Returns true if the panel is in this region.
44105      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44106      * @return {Boolean}
44107      */
44108     hasPanel : function(panel){
44109         if(typeof panel == "object"){ // must be panel obj
44110             panel = panel.getId();
44111         }
44112         return this.getPanel(panel) ? true : false;
44113     },
44114     
44115     /**
44116      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44117      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44118      * @param {Boolean} preservePanel Overrides the config preservePanel option
44119      * @return {Roo.ContentPanel} The panel that was removed
44120      */
44121     remove : function(panel, preservePanel){
44122         panel = this.getPanel(panel);
44123         if(!panel){
44124             return null;
44125         }
44126         var e = {};
44127         this.fireEvent("beforeremove", this, panel, e);
44128         if(e.cancel === true){
44129             return null;
44130         }
44131         var panelId = panel.getId();
44132         this.panels.removeKey(panelId);
44133         return panel;
44134     },
44135     
44136     /**
44137      * Returns the panel specified or null if it's not in this region.
44138      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44139      * @return {Roo.ContentPanel}
44140      */
44141     getPanel : function(id){
44142         if(typeof id == "object"){ // must be panel obj
44143             return id;
44144         }
44145         return this.panels.get(id);
44146     },
44147     
44148     /**
44149      * Returns this regions position (north/south/east/west/center).
44150      * @return {String} 
44151      */
44152     getPosition: function(){
44153         return this.position;    
44154     }
44155 });/*
44156  * Based on:
44157  * Ext JS Library 1.1.1
44158  * Copyright(c) 2006-2007, Ext JS, LLC.
44159  *
44160  * Originally Released Under LGPL - original licence link has changed is not relivant.
44161  *
44162  * Fork - LGPL
44163  * <script type="text/javascript">
44164  */
44165  
44166 /**
44167  * @class Roo.LayoutRegion
44168  * @extends Roo.BasicLayoutRegion
44169  * This class represents a region in a layout manager.
44170  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
44171  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
44172  * @cfg {Boolean} floatable False to disable floating (defaults to true)
44173  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44174  * @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})
44175  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
44176  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
44177  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
44178  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
44179  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
44180  * @cfg {String} title The title for the region (overrides panel titles)
44181  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
44182  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44183  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
44184  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44185  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
44186  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44187  * the space available, similar to FireFox 1.5 tabs (defaults to false)
44188  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
44189  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
44190  * @cfg {Boolean} showPin True to show a pin button
44191 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
44192 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
44193 * @cfg {Boolean} disableTabTips True to disable tab tooltips
44194 * @cfg {Number} width  For East/West panels
44195 * @cfg {Number} height For North/South panels
44196 * @cfg {Boolean} split To show the splitter
44197  */
44198 Roo.LayoutRegion = function(mgr, config, pos){
44199     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
44200     var dh = Roo.DomHelper;
44201     /** This region's container element 
44202     * @type Roo.Element */
44203     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
44204     /** This region's title element 
44205     * @type Roo.Element */
44206
44207     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
44208         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44209         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
44210     ]}, true);
44211     this.titleEl.enableDisplayMode();
44212     /** This region's title text element 
44213     * @type HTMLElement */
44214     this.titleTextEl = this.titleEl.dom.firstChild;
44215     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44216     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
44217     this.closeBtn.enableDisplayMode();
44218     this.closeBtn.on("click", this.closeClicked, this);
44219     this.closeBtn.hide();
44220
44221     this.createBody(config);
44222     this.visible = true;
44223     this.collapsed = false;
44224
44225     if(config.hideWhenEmpty){
44226         this.hide();
44227         this.on("paneladded", this.validateVisibility, this);
44228         this.on("panelremoved", this.validateVisibility, this);
44229     }
44230     this.applyConfig(config);
44231 };
44232
44233 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
44234
44235     createBody : function(){
44236         /** This region's body element 
44237         * @type Roo.Element */
44238         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
44239     },
44240
44241     applyConfig : function(c){
44242         if(c.collapsible && this.position != "center" && !this.collapsedEl){
44243             var dh = Roo.DomHelper;
44244             if(c.titlebar !== false){
44245                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
44246                 this.collapseBtn.on("click", this.collapse, this);
44247                 this.collapseBtn.enableDisplayMode();
44248
44249                 if(c.showPin === true || this.showPin){
44250                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
44251                     this.stickBtn.enableDisplayMode();
44252                     this.stickBtn.on("click", this.expand, this);
44253                     this.stickBtn.hide();
44254                 }
44255             }
44256             /** This region's collapsed element
44257             * @type Roo.Element */
44258             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44259                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44260             ]}, true);
44261             if(c.floatable !== false){
44262                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44263                this.collapsedEl.on("click", this.collapseClick, this);
44264             }
44265
44266             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44267                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44268                    id: "message", unselectable: "on", style:{"float":"left"}});
44269                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44270              }
44271             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44272             this.expandBtn.on("click", this.expand, this);
44273         }
44274         if(this.collapseBtn){
44275             this.collapseBtn.setVisible(c.collapsible == true);
44276         }
44277         this.cmargins = c.cmargins || this.cmargins ||
44278                          (this.position == "west" || this.position == "east" ?
44279                              {top: 0, left: 2, right:2, bottom: 0} :
44280                              {top: 2, left: 0, right:0, bottom: 2});
44281         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44282         this.bottomTabs = c.tabPosition != "top";
44283         this.autoScroll = c.autoScroll || false;
44284         if(this.autoScroll){
44285             this.bodyEl.setStyle("overflow", "auto");
44286         }else{
44287             this.bodyEl.setStyle("overflow", "hidden");
44288         }
44289         //if(c.titlebar !== false){
44290             if((!c.titlebar && !c.title) || c.titlebar === false){
44291                 this.titleEl.hide();
44292             }else{
44293                 this.titleEl.show();
44294                 if(c.title){
44295                     this.titleTextEl.innerHTML = c.title;
44296                 }
44297             }
44298         //}
44299         this.duration = c.duration || .30;
44300         this.slideDuration = c.slideDuration || .45;
44301         this.config = c;
44302         if(c.collapsed){
44303             this.collapse(true);
44304         }
44305         if(c.hidden){
44306             this.hide();
44307         }
44308     },
44309     /**
44310      * Returns true if this region is currently visible.
44311      * @return {Boolean}
44312      */
44313     isVisible : function(){
44314         return this.visible;
44315     },
44316
44317     /**
44318      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44319      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44320      */
44321     setCollapsedTitle : function(title){
44322         title = title || "&#160;";
44323         if(this.collapsedTitleTextEl){
44324             this.collapsedTitleTextEl.innerHTML = title;
44325         }
44326     },
44327
44328     getBox : function(){
44329         var b;
44330         if(!this.collapsed){
44331             b = this.el.getBox(false, true);
44332         }else{
44333             b = this.collapsedEl.getBox(false, true);
44334         }
44335         return b;
44336     },
44337
44338     getMargins : function(){
44339         return this.collapsed ? this.cmargins : this.margins;
44340     },
44341
44342     highlight : function(){
44343         this.el.addClass("x-layout-panel-dragover");
44344     },
44345
44346     unhighlight : function(){
44347         this.el.removeClass("x-layout-panel-dragover");
44348     },
44349
44350     updateBox : function(box){
44351         this.box = box;
44352         if(!this.collapsed){
44353             this.el.dom.style.left = box.x + "px";
44354             this.el.dom.style.top = box.y + "px";
44355             this.updateBody(box.width, box.height);
44356         }else{
44357             this.collapsedEl.dom.style.left = box.x + "px";
44358             this.collapsedEl.dom.style.top = box.y + "px";
44359             this.collapsedEl.setSize(box.width, box.height);
44360         }
44361         if(this.tabs){
44362             this.tabs.autoSizeTabs();
44363         }
44364     },
44365
44366     updateBody : function(w, h){
44367         if(w !== null){
44368             this.el.setWidth(w);
44369             w -= this.el.getBorderWidth("rl");
44370             if(this.config.adjustments){
44371                 w += this.config.adjustments[0];
44372             }
44373         }
44374         if(h !== null){
44375             this.el.setHeight(h);
44376             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44377             h -= this.el.getBorderWidth("tb");
44378             if(this.config.adjustments){
44379                 h += this.config.adjustments[1];
44380             }
44381             this.bodyEl.setHeight(h);
44382             if(this.tabs){
44383                 h = this.tabs.syncHeight(h);
44384             }
44385         }
44386         if(this.panelSize){
44387             w = w !== null ? w : this.panelSize.width;
44388             h = h !== null ? h : this.panelSize.height;
44389         }
44390         if(this.activePanel){
44391             var el = this.activePanel.getEl();
44392             w = w !== null ? w : el.getWidth();
44393             h = h !== null ? h : el.getHeight();
44394             this.panelSize = {width: w, height: h};
44395             this.activePanel.setSize(w, h);
44396         }
44397         if(Roo.isIE && this.tabs){
44398             this.tabs.el.repaint();
44399         }
44400     },
44401
44402     /**
44403      * Returns the container element for this region.
44404      * @return {Roo.Element}
44405      */
44406     getEl : function(){
44407         return this.el;
44408     },
44409
44410     /**
44411      * Hides this region.
44412      */
44413     hide : function(){
44414         if(!this.collapsed){
44415             this.el.dom.style.left = "-2000px";
44416             this.el.hide();
44417         }else{
44418             this.collapsedEl.dom.style.left = "-2000px";
44419             this.collapsedEl.hide();
44420         }
44421         this.visible = false;
44422         this.fireEvent("visibilitychange", this, false);
44423     },
44424
44425     /**
44426      * Shows this region if it was previously hidden.
44427      */
44428     show : function(){
44429         if(!this.collapsed){
44430             this.el.show();
44431         }else{
44432             this.collapsedEl.show();
44433         }
44434         this.visible = true;
44435         this.fireEvent("visibilitychange", this, true);
44436     },
44437
44438     closeClicked : function(){
44439         if(this.activePanel){
44440             this.remove(this.activePanel);
44441         }
44442     },
44443
44444     collapseClick : function(e){
44445         if(this.isSlid){
44446            e.stopPropagation();
44447            this.slideIn();
44448         }else{
44449            e.stopPropagation();
44450            this.slideOut();
44451         }
44452     },
44453
44454     /**
44455      * Collapses this region.
44456      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44457      */
44458     collapse : function(skipAnim){
44459         if(this.collapsed) return;
44460         this.collapsed = true;
44461         if(this.split){
44462             this.split.el.hide();
44463         }
44464         if(this.config.animate && skipAnim !== true){
44465             this.fireEvent("invalidated", this);
44466             this.animateCollapse();
44467         }else{
44468             this.el.setLocation(-20000,-20000);
44469             this.el.hide();
44470             this.collapsedEl.show();
44471             this.fireEvent("collapsed", this);
44472             this.fireEvent("invalidated", this);
44473         }
44474     },
44475
44476     animateCollapse : function(){
44477         // overridden
44478     },
44479
44480     /**
44481      * Expands this region if it was previously collapsed.
44482      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44483      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44484      */
44485     expand : function(e, skipAnim){
44486         if(e) e.stopPropagation();
44487         if(!this.collapsed || this.el.hasActiveFx()) return;
44488         if(this.isSlid){
44489             this.afterSlideIn();
44490             skipAnim = true;
44491         }
44492         this.collapsed = false;
44493         if(this.config.animate && skipAnim !== true){
44494             this.animateExpand();
44495         }else{
44496             this.el.show();
44497             if(this.split){
44498                 this.split.el.show();
44499             }
44500             this.collapsedEl.setLocation(-2000,-2000);
44501             this.collapsedEl.hide();
44502             this.fireEvent("invalidated", this);
44503             this.fireEvent("expanded", this);
44504         }
44505     },
44506
44507     animateExpand : function(){
44508         // overridden
44509     },
44510
44511     initTabs : function(){
44512         this.bodyEl.setStyle("overflow", "hidden");
44513         var ts = new Roo.TabPanel(this.bodyEl.dom, {
44514             tabPosition: this.bottomTabs ? 'bottom' : 'top',
44515             disableTooltips: this.config.disableTabTips
44516         });
44517         if(this.config.hideTabs){
44518             ts.stripWrap.setDisplayed(false);
44519         }
44520         this.tabs = ts;
44521         ts.resizeTabs = this.config.resizeTabs === true;
44522         ts.minTabWidth = this.config.minTabWidth || 40;
44523         ts.maxTabWidth = this.config.maxTabWidth || 250;
44524         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44525         ts.monitorResize = false;
44526         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44527         ts.bodyEl.addClass('x-layout-tabs-body');
44528         this.panels.each(this.initPanelAsTab, this);
44529     },
44530
44531     initPanelAsTab : function(panel){
44532         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44533                     this.config.closeOnTab && panel.isClosable());
44534         if(panel.tabTip !== undefined){
44535             ti.setTooltip(panel.tabTip);
44536         }
44537         ti.on("activate", function(){
44538               this.setActivePanel(panel);
44539         }, this);
44540         if(this.config.closeOnTab){
44541             ti.on("beforeclose", function(t, e){
44542                 e.cancel = true;
44543                 this.remove(panel);
44544             }, this);
44545         }
44546         return ti;
44547     },
44548
44549     updatePanelTitle : function(panel, title){
44550         if(this.activePanel == panel){
44551             this.updateTitle(title);
44552         }
44553         if(this.tabs){
44554             var ti = this.tabs.getTab(panel.getEl().id);
44555             ti.setText(title);
44556             if(panel.tabTip !== undefined){
44557                 ti.setTooltip(panel.tabTip);
44558             }
44559         }
44560     },
44561
44562     updateTitle : function(title){
44563         if(this.titleTextEl && !this.config.title){
44564             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44565         }
44566     },
44567
44568     setActivePanel : function(panel){
44569         panel = this.getPanel(panel);
44570         if(this.activePanel && this.activePanel != panel){
44571             this.activePanel.setActiveState(false);
44572         }
44573         this.activePanel = panel;
44574         panel.setActiveState(true);
44575         if(this.panelSize){
44576             panel.setSize(this.panelSize.width, this.panelSize.height);
44577         }
44578         if(this.closeBtn){
44579             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44580         }
44581         this.updateTitle(panel.getTitle());
44582         if(this.tabs){
44583             this.fireEvent("invalidated", this);
44584         }
44585         this.fireEvent("panelactivated", this, panel);
44586     },
44587
44588     /**
44589      * Shows the specified panel.
44590      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44591      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44592      */
44593     showPanel : function(panel){
44594         if(panel = this.getPanel(panel)){
44595             if(this.tabs){
44596                 var tab = this.tabs.getTab(panel.getEl().id);
44597                 if(tab.isHidden()){
44598                     this.tabs.unhideTab(tab.id);
44599                 }
44600                 tab.activate();
44601             }else{
44602                 this.setActivePanel(panel);
44603             }
44604         }
44605         return panel;
44606     },
44607
44608     /**
44609      * Get the active panel for this region.
44610      * @return {Roo.ContentPanel} The active panel or null
44611      */
44612     getActivePanel : function(){
44613         return this.activePanel;
44614     },
44615
44616     validateVisibility : function(){
44617         if(this.panels.getCount() < 1){
44618             this.updateTitle("&#160;");
44619             this.closeBtn.hide();
44620             this.hide();
44621         }else{
44622             if(!this.isVisible()){
44623                 this.show();
44624             }
44625         }
44626     },
44627
44628     /**
44629      * Adds the passed ContentPanel(s) to this region.
44630      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44631      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44632      */
44633     add : function(panel){
44634         if(arguments.length > 1){
44635             for(var i = 0, len = arguments.length; i < len; i++) {
44636                 this.add(arguments[i]);
44637             }
44638             return null;
44639         }
44640         if(this.hasPanel(panel)){
44641             this.showPanel(panel);
44642             return panel;
44643         }
44644         panel.setRegion(this);
44645         this.panels.add(panel);
44646         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44647             this.bodyEl.dom.appendChild(panel.getEl().dom);
44648             if(panel.background !== true){
44649                 this.setActivePanel(panel);
44650             }
44651             this.fireEvent("paneladded", this, panel);
44652             return panel;
44653         }
44654         if(!this.tabs){
44655             this.initTabs();
44656         }else{
44657             this.initPanelAsTab(panel);
44658         }
44659         if(panel.background !== true){
44660             this.tabs.activate(panel.getEl().id);
44661         }
44662         this.fireEvent("paneladded", this, panel);
44663         return panel;
44664     },
44665
44666     /**
44667      * Hides the tab for the specified panel.
44668      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44669      */
44670     hidePanel : function(panel){
44671         if(this.tabs && (panel = this.getPanel(panel))){
44672             this.tabs.hideTab(panel.getEl().id);
44673         }
44674     },
44675
44676     /**
44677      * Unhides the tab for a previously hidden panel.
44678      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44679      */
44680     unhidePanel : function(panel){
44681         if(this.tabs && (panel = this.getPanel(panel))){
44682             this.tabs.unhideTab(panel.getEl().id);
44683         }
44684     },
44685
44686     clearPanels : function(){
44687         while(this.panels.getCount() > 0){
44688              this.remove(this.panels.first());
44689         }
44690     },
44691
44692     /**
44693      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44694      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44695      * @param {Boolean} preservePanel Overrides the config preservePanel option
44696      * @return {Roo.ContentPanel} The panel that was removed
44697      */
44698     remove : function(panel, preservePanel){
44699         panel = this.getPanel(panel);
44700         if(!panel){
44701             return null;
44702         }
44703         var e = {};
44704         this.fireEvent("beforeremove", this, panel, e);
44705         if(e.cancel === true){
44706             return null;
44707         }
44708         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44709         var panelId = panel.getId();
44710         this.panels.removeKey(panelId);
44711         if(preservePanel){
44712             document.body.appendChild(panel.getEl().dom);
44713         }
44714         if(this.tabs){
44715             this.tabs.removeTab(panel.getEl().id);
44716         }else if (!preservePanel){
44717             this.bodyEl.dom.removeChild(panel.getEl().dom);
44718         }
44719         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44720             var p = this.panels.first();
44721             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44722             tempEl.appendChild(p.getEl().dom);
44723             this.bodyEl.update("");
44724             this.bodyEl.dom.appendChild(p.getEl().dom);
44725             tempEl = null;
44726             this.updateTitle(p.getTitle());
44727             this.tabs = null;
44728             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44729             this.setActivePanel(p);
44730         }
44731         panel.setRegion(null);
44732         if(this.activePanel == panel){
44733             this.activePanel = null;
44734         }
44735         if(this.config.autoDestroy !== false && preservePanel !== true){
44736             try{panel.destroy();}catch(e){}
44737         }
44738         this.fireEvent("panelremoved", this, panel);
44739         return panel;
44740     },
44741
44742     /**
44743      * Returns the TabPanel component used by this region
44744      * @return {Roo.TabPanel}
44745      */
44746     getTabs : function(){
44747         return this.tabs;
44748     },
44749
44750     createTool : function(parentEl, className){
44751         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
44752             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
44753         btn.addClassOnOver("x-layout-tools-button-over");
44754         return btn;
44755     }
44756 });/*
44757  * Based on:
44758  * Ext JS Library 1.1.1
44759  * Copyright(c) 2006-2007, Ext JS, LLC.
44760  *
44761  * Originally Released Under LGPL - original licence link has changed is not relivant.
44762  *
44763  * Fork - LGPL
44764  * <script type="text/javascript">
44765  */
44766  
44767
44768
44769 /**
44770  * @class Roo.SplitLayoutRegion
44771  * @extends Roo.LayoutRegion
44772  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44773  */
44774 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
44775     this.cursor = cursor;
44776     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
44777 };
44778
44779 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
44780     splitTip : "Drag to resize.",
44781     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44782     useSplitTips : false,
44783
44784     applyConfig : function(config){
44785         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
44786         if(config.split){
44787             if(!this.split){
44788                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
44789                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
44790                 /** The SplitBar for this region 
44791                 * @type Roo.SplitBar */
44792                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
44793                 this.split.on("moved", this.onSplitMove, this);
44794                 this.split.useShim = config.useShim === true;
44795                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44796                 if(this.useSplitTips){
44797                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44798                 }
44799                 if(config.collapsible){
44800                     this.split.el.on("dblclick", this.collapse,  this);
44801                 }
44802             }
44803             if(typeof config.minSize != "undefined"){
44804                 this.split.minSize = config.minSize;
44805             }
44806             if(typeof config.maxSize != "undefined"){
44807                 this.split.maxSize = config.maxSize;
44808             }
44809             if(config.hideWhenEmpty || config.hidden || config.collapsed){
44810                 this.hideSplitter();
44811             }
44812         }
44813     },
44814
44815     getHMaxSize : function(){
44816          var cmax = this.config.maxSize || 10000;
44817          var center = this.mgr.getRegion("center");
44818          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44819     },
44820
44821     getVMaxSize : function(){
44822          var cmax = this.config.maxSize || 10000;
44823          var center = this.mgr.getRegion("center");
44824          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44825     },
44826
44827     onSplitMove : function(split, newSize){
44828         this.fireEvent("resized", this, newSize);
44829     },
44830     
44831     /** 
44832      * Returns the {@link Roo.SplitBar} for this region.
44833      * @return {Roo.SplitBar}
44834      */
44835     getSplitBar : function(){
44836         return this.split;
44837     },
44838     
44839     hide : function(){
44840         this.hideSplitter();
44841         Roo.SplitLayoutRegion.superclass.hide.call(this);
44842     },
44843
44844     hideSplitter : function(){
44845         if(this.split){
44846             this.split.el.setLocation(-2000,-2000);
44847             this.split.el.hide();
44848         }
44849     },
44850
44851     show : function(){
44852         if(this.split){
44853             this.split.el.show();
44854         }
44855         Roo.SplitLayoutRegion.superclass.show.call(this);
44856     },
44857     
44858     beforeSlide: function(){
44859         if(Roo.isGecko){// firefox overflow auto bug workaround
44860             this.bodyEl.clip();
44861             if(this.tabs) this.tabs.bodyEl.clip();
44862             if(this.activePanel){
44863                 this.activePanel.getEl().clip();
44864                 
44865                 if(this.activePanel.beforeSlide){
44866                     this.activePanel.beforeSlide();
44867                 }
44868             }
44869         }
44870     },
44871     
44872     afterSlide : function(){
44873         if(Roo.isGecko){// firefox overflow auto bug workaround
44874             this.bodyEl.unclip();
44875             if(this.tabs) this.tabs.bodyEl.unclip();
44876             if(this.activePanel){
44877                 this.activePanel.getEl().unclip();
44878                 if(this.activePanel.afterSlide){
44879                     this.activePanel.afterSlide();
44880                 }
44881             }
44882         }
44883     },
44884
44885     initAutoHide : function(){
44886         if(this.autoHide !== false){
44887             if(!this.autoHideHd){
44888                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44889                 this.autoHideHd = {
44890                     "mouseout": function(e){
44891                         if(!e.within(this.el, true)){
44892                             st.delay(500);
44893                         }
44894                     },
44895                     "mouseover" : function(e){
44896                         st.cancel();
44897                     },
44898                     scope : this
44899                 };
44900             }
44901             this.el.on(this.autoHideHd);
44902         }
44903     },
44904
44905     clearAutoHide : function(){
44906         if(this.autoHide !== false){
44907             this.el.un("mouseout", this.autoHideHd.mouseout);
44908             this.el.un("mouseover", this.autoHideHd.mouseover);
44909         }
44910     },
44911
44912     clearMonitor : function(){
44913         Roo.get(document).un("click", this.slideInIf, this);
44914     },
44915
44916     // these names are backwards but not changed for compat
44917     slideOut : function(){
44918         if(this.isSlid || this.el.hasActiveFx()){
44919             return;
44920         }
44921         this.isSlid = true;
44922         if(this.collapseBtn){
44923             this.collapseBtn.hide();
44924         }
44925         this.closeBtnState = this.closeBtn.getStyle('display');
44926         this.closeBtn.hide();
44927         if(this.stickBtn){
44928             this.stickBtn.show();
44929         }
44930         this.el.show();
44931         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44932         this.beforeSlide();
44933         this.el.setStyle("z-index", 10001);
44934         this.el.slideIn(this.getSlideAnchor(), {
44935             callback: function(){
44936                 this.afterSlide();
44937                 this.initAutoHide();
44938                 Roo.get(document).on("click", this.slideInIf, this);
44939                 this.fireEvent("slideshow", this);
44940             },
44941             scope: this,
44942             block: true
44943         });
44944     },
44945
44946     afterSlideIn : function(){
44947         this.clearAutoHide();
44948         this.isSlid = false;
44949         this.clearMonitor();
44950         this.el.setStyle("z-index", "");
44951         if(this.collapseBtn){
44952             this.collapseBtn.show();
44953         }
44954         this.closeBtn.setStyle('display', this.closeBtnState);
44955         if(this.stickBtn){
44956             this.stickBtn.hide();
44957         }
44958         this.fireEvent("slidehide", this);
44959     },
44960
44961     slideIn : function(cb){
44962         if(!this.isSlid || this.el.hasActiveFx()){
44963             Roo.callback(cb);
44964             return;
44965         }
44966         this.isSlid = false;
44967         this.beforeSlide();
44968         this.el.slideOut(this.getSlideAnchor(), {
44969             callback: function(){
44970                 this.el.setLeftTop(-10000, -10000);
44971                 this.afterSlide();
44972                 this.afterSlideIn();
44973                 Roo.callback(cb);
44974             },
44975             scope: this,
44976             block: true
44977         });
44978     },
44979     
44980     slideInIf : function(e){
44981         if(!e.within(this.el)){
44982             this.slideIn();
44983         }
44984     },
44985
44986     animateCollapse : function(){
44987         this.beforeSlide();
44988         this.el.setStyle("z-index", 20000);
44989         var anchor = this.getSlideAnchor();
44990         this.el.slideOut(anchor, {
44991             callback : function(){
44992                 this.el.setStyle("z-index", "");
44993                 this.collapsedEl.slideIn(anchor, {duration:.3});
44994                 this.afterSlide();
44995                 this.el.setLocation(-10000,-10000);
44996                 this.el.hide();
44997                 this.fireEvent("collapsed", this);
44998             },
44999             scope: this,
45000             block: true
45001         });
45002     },
45003
45004     animateExpand : function(){
45005         this.beforeSlide();
45006         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
45007         this.el.setStyle("z-index", 20000);
45008         this.collapsedEl.hide({
45009             duration:.1
45010         });
45011         this.el.slideIn(this.getSlideAnchor(), {
45012             callback : function(){
45013                 this.el.setStyle("z-index", "");
45014                 this.afterSlide();
45015                 if(this.split){
45016                     this.split.el.show();
45017                 }
45018                 this.fireEvent("invalidated", this);
45019                 this.fireEvent("expanded", this);
45020             },
45021             scope: this,
45022             block: true
45023         });
45024     },
45025
45026     anchors : {
45027         "west" : "left",
45028         "east" : "right",
45029         "north" : "top",
45030         "south" : "bottom"
45031     },
45032
45033     sanchors : {
45034         "west" : "l",
45035         "east" : "r",
45036         "north" : "t",
45037         "south" : "b"
45038     },
45039
45040     canchors : {
45041         "west" : "tl-tr",
45042         "east" : "tr-tl",
45043         "north" : "tl-bl",
45044         "south" : "bl-tl"
45045     },
45046
45047     getAnchor : function(){
45048         return this.anchors[this.position];
45049     },
45050
45051     getCollapseAnchor : function(){
45052         return this.canchors[this.position];
45053     },
45054
45055     getSlideAnchor : function(){
45056         return this.sanchors[this.position];
45057     },
45058
45059     getAlignAdj : function(){
45060         var cm = this.cmargins;
45061         switch(this.position){
45062             case "west":
45063                 return [0, 0];
45064             break;
45065             case "east":
45066                 return [0, 0];
45067             break;
45068             case "north":
45069                 return [0, 0];
45070             break;
45071             case "south":
45072                 return [0, 0];
45073             break;
45074         }
45075     },
45076
45077     getExpandAdj : function(){
45078         var c = this.collapsedEl, cm = this.cmargins;
45079         switch(this.position){
45080             case "west":
45081                 return [-(cm.right+c.getWidth()+cm.left), 0];
45082             break;
45083             case "east":
45084                 return [cm.right+c.getWidth()+cm.left, 0];
45085             break;
45086             case "north":
45087                 return [0, -(cm.top+cm.bottom+c.getHeight())];
45088             break;
45089             case "south":
45090                 return [0, cm.top+cm.bottom+c.getHeight()];
45091             break;
45092         }
45093     }
45094 });/*
45095  * Based on:
45096  * Ext JS Library 1.1.1
45097  * Copyright(c) 2006-2007, Ext JS, LLC.
45098  *
45099  * Originally Released Under LGPL - original licence link has changed is not relivant.
45100  *
45101  * Fork - LGPL
45102  * <script type="text/javascript">
45103  */
45104 /*
45105  * These classes are private internal classes
45106  */
45107 Roo.CenterLayoutRegion = function(mgr, config){
45108     Roo.LayoutRegion.call(this, mgr, config, "center");
45109     this.visible = true;
45110     this.minWidth = config.minWidth || 20;
45111     this.minHeight = config.minHeight || 20;
45112 };
45113
45114 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
45115     hide : function(){
45116         // center panel can't be hidden
45117     },
45118     
45119     show : function(){
45120         // center panel can't be hidden
45121     },
45122     
45123     getMinWidth: function(){
45124         return this.minWidth;
45125     },
45126     
45127     getMinHeight: function(){
45128         return this.minHeight;
45129     }
45130 });
45131
45132
45133 Roo.NorthLayoutRegion = function(mgr, config){
45134     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
45135     if(this.split){
45136         this.split.placement = Roo.SplitBar.TOP;
45137         this.split.orientation = Roo.SplitBar.VERTICAL;
45138         this.split.el.addClass("x-layout-split-v");
45139     }
45140     var size = config.initialSize || config.height;
45141     if(typeof size != "undefined"){
45142         this.el.setHeight(size);
45143     }
45144 };
45145 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
45146     orientation: Roo.SplitBar.VERTICAL,
45147     getBox : function(){
45148         if(this.collapsed){
45149             return this.collapsedEl.getBox();
45150         }
45151         var box = this.el.getBox();
45152         if(this.split){
45153             box.height += this.split.el.getHeight();
45154         }
45155         return box;
45156     },
45157     
45158     updateBox : function(box){
45159         if(this.split && !this.collapsed){
45160             box.height -= this.split.el.getHeight();
45161             this.split.el.setLeft(box.x);
45162             this.split.el.setTop(box.y+box.height);
45163             this.split.el.setWidth(box.width);
45164         }
45165         if(this.collapsed){
45166             this.updateBody(box.width, null);
45167         }
45168         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45169     }
45170 });
45171
45172 Roo.SouthLayoutRegion = function(mgr, config){
45173     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
45174     if(this.split){
45175         this.split.placement = Roo.SplitBar.BOTTOM;
45176         this.split.orientation = Roo.SplitBar.VERTICAL;
45177         this.split.el.addClass("x-layout-split-v");
45178     }
45179     var size = config.initialSize || config.height;
45180     if(typeof size != "undefined"){
45181         this.el.setHeight(size);
45182     }
45183 };
45184 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
45185     orientation: Roo.SplitBar.VERTICAL,
45186     getBox : function(){
45187         if(this.collapsed){
45188             return this.collapsedEl.getBox();
45189         }
45190         var box = this.el.getBox();
45191         if(this.split){
45192             var sh = this.split.el.getHeight();
45193             box.height += sh;
45194             box.y -= sh;
45195         }
45196         return box;
45197     },
45198     
45199     updateBox : function(box){
45200         if(this.split && !this.collapsed){
45201             var sh = this.split.el.getHeight();
45202             box.height -= sh;
45203             box.y += sh;
45204             this.split.el.setLeft(box.x);
45205             this.split.el.setTop(box.y-sh);
45206             this.split.el.setWidth(box.width);
45207         }
45208         if(this.collapsed){
45209             this.updateBody(box.width, null);
45210         }
45211         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45212     }
45213 });
45214
45215 Roo.EastLayoutRegion = function(mgr, config){
45216     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
45217     if(this.split){
45218         this.split.placement = Roo.SplitBar.RIGHT;
45219         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45220         this.split.el.addClass("x-layout-split-h");
45221     }
45222     var size = config.initialSize || config.width;
45223     if(typeof size != "undefined"){
45224         this.el.setWidth(size);
45225     }
45226 };
45227 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
45228     orientation: Roo.SplitBar.HORIZONTAL,
45229     getBox : function(){
45230         if(this.collapsed){
45231             return this.collapsedEl.getBox();
45232         }
45233         var box = this.el.getBox();
45234         if(this.split){
45235             var sw = this.split.el.getWidth();
45236             box.width += sw;
45237             box.x -= sw;
45238         }
45239         return box;
45240     },
45241
45242     updateBox : function(box){
45243         if(this.split && !this.collapsed){
45244             var sw = this.split.el.getWidth();
45245             box.width -= sw;
45246             this.split.el.setLeft(box.x);
45247             this.split.el.setTop(box.y);
45248             this.split.el.setHeight(box.height);
45249             box.x += sw;
45250         }
45251         if(this.collapsed){
45252             this.updateBody(null, box.height);
45253         }
45254         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45255     }
45256 });
45257
45258 Roo.WestLayoutRegion = function(mgr, config){
45259     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
45260     if(this.split){
45261         this.split.placement = Roo.SplitBar.LEFT;
45262         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45263         this.split.el.addClass("x-layout-split-h");
45264     }
45265     var size = config.initialSize || config.width;
45266     if(typeof size != "undefined"){
45267         this.el.setWidth(size);
45268     }
45269 };
45270 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
45271     orientation: Roo.SplitBar.HORIZONTAL,
45272     getBox : function(){
45273         if(this.collapsed){
45274             return this.collapsedEl.getBox();
45275         }
45276         var box = this.el.getBox();
45277         if(this.split){
45278             box.width += this.split.el.getWidth();
45279         }
45280         return box;
45281     },
45282     
45283     updateBox : function(box){
45284         if(this.split && !this.collapsed){
45285             var sw = this.split.el.getWidth();
45286             box.width -= sw;
45287             this.split.el.setLeft(box.x+box.width);
45288             this.split.el.setTop(box.y);
45289             this.split.el.setHeight(box.height);
45290         }
45291         if(this.collapsed){
45292             this.updateBody(null, box.height);
45293         }
45294         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45295     }
45296 });
45297 /*
45298  * Based on:
45299  * Ext JS Library 1.1.1
45300  * Copyright(c) 2006-2007, Ext JS, LLC.
45301  *
45302  * Originally Released Under LGPL - original licence link has changed is not relivant.
45303  *
45304  * Fork - LGPL
45305  * <script type="text/javascript">
45306  */
45307  
45308  
45309 /*
45310  * Private internal class for reading and applying state
45311  */
45312 Roo.LayoutStateManager = function(layout){
45313      // default empty state
45314      this.state = {
45315         north: {},
45316         south: {},
45317         east: {},
45318         west: {}       
45319     };
45320 };
45321
45322 Roo.LayoutStateManager.prototype = {
45323     init : function(layout, provider){
45324         this.provider = provider;
45325         var state = provider.get(layout.id+"-layout-state");
45326         if(state){
45327             var wasUpdating = layout.isUpdating();
45328             if(!wasUpdating){
45329                 layout.beginUpdate();
45330             }
45331             for(var key in state){
45332                 if(typeof state[key] != "function"){
45333                     var rstate = state[key];
45334                     var r = layout.getRegion(key);
45335                     if(r && rstate){
45336                         if(rstate.size){
45337                             r.resizeTo(rstate.size);
45338                         }
45339                         if(rstate.collapsed == true){
45340                             r.collapse(true);
45341                         }else{
45342                             r.expand(null, true);
45343                         }
45344                     }
45345                 }
45346             }
45347             if(!wasUpdating){
45348                 layout.endUpdate();
45349             }
45350             this.state = state; 
45351         }
45352         this.layout = layout;
45353         layout.on("regionresized", this.onRegionResized, this);
45354         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45355         layout.on("regionexpanded", this.onRegionExpanded, this);
45356     },
45357     
45358     storeState : function(){
45359         this.provider.set(this.layout.id+"-layout-state", this.state);
45360     },
45361     
45362     onRegionResized : function(region, newSize){
45363         this.state[region.getPosition()].size = newSize;
45364         this.storeState();
45365     },
45366     
45367     onRegionCollapsed : function(region){
45368         this.state[region.getPosition()].collapsed = true;
45369         this.storeState();
45370     },
45371     
45372     onRegionExpanded : function(region){
45373         this.state[region.getPosition()].collapsed = false;
45374         this.storeState();
45375     }
45376 };/*
45377  * Based on:
45378  * Ext JS Library 1.1.1
45379  * Copyright(c) 2006-2007, Ext JS, LLC.
45380  *
45381  * Originally Released Under LGPL - original licence link has changed is not relivant.
45382  *
45383  * Fork - LGPL
45384  * <script type="text/javascript">
45385  */
45386 /**
45387  * @class Roo.ContentPanel
45388  * @extends Roo.util.Observable
45389  * A basic ContentPanel element.
45390  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45391  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45392  * @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
45393  * @cfg {Boolean} closable True if the panel can be closed/removed
45394  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45395  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45396  * @cfg {Toolbar} toolbar A toolbar for this panel
45397  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45398  * @cfg {String} title The title for this panel
45399  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45400  * @cfg {String} url Calls {@link #setUrl} with this value
45401  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45402  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45403  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45404  * @constructor
45405  * Create a new ContentPanel.
45406  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45407  * @param {String/Object} config A string to set only the title or a config object
45408  * @param {String} content (optional) Set the HTML content for this panel
45409  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45410  */
45411 Roo.ContentPanel = function(el, config, content){
45412     
45413      
45414     /*
45415     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45416         config = el;
45417         el = Roo.id();
45418     }
45419     if (config && config.parentLayout) { 
45420         el = config.parentLayout.el.createChild(); 
45421     }
45422     */
45423     if(el.autoCreate){ // xtype is available if this is called from factory
45424         config = el;
45425         el = Roo.id();
45426     }
45427     this.el = Roo.get(el);
45428     if(!this.el && config && config.autoCreate){
45429         if(typeof config.autoCreate == "object"){
45430             if(!config.autoCreate.id){
45431                 config.autoCreate.id = config.id||el;
45432             }
45433             this.el = Roo.DomHelper.append(document.body,
45434                         config.autoCreate, true);
45435         }else{
45436             this.el = Roo.DomHelper.append(document.body,
45437                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45438         }
45439     }
45440     this.closable = false;
45441     this.loaded = false;
45442     this.active = false;
45443     if(typeof config == "string"){
45444         this.title = config;
45445     }else{
45446         Roo.apply(this, config);
45447     }
45448     
45449     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45450         this.wrapEl = this.el.wrap();    
45451         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45452         
45453     }
45454     
45455     
45456     
45457     if(this.resizeEl){
45458         this.resizeEl = Roo.get(this.resizeEl, true);
45459     }else{
45460         this.resizeEl = this.el;
45461     }
45462     this.addEvents({
45463         /**
45464          * @event activate
45465          * Fires when this panel is activated. 
45466          * @param {Roo.ContentPanel} this
45467          */
45468         "activate" : true,
45469         /**
45470          * @event deactivate
45471          * Fires when this panel is activated. 
45472          * @param {Roo.ContentPanel} this
45473          */
45474         "deactivate" : true,
45475
45476         /**
45477          * @event resize
45478          * Fires when this panel is resized if fitToFrame is true.
45479          * @param {Roo.ContentPanel} this
45480          * @param {Number} width The width after any component adjustments
45481          * @param {Number} height The height after any component adjustments
45482          */
45483         "resize" : true
45484     });
45485     if(this.autoScroll){
45486         this.resizeEl.setStyle("overflow", "auto");
45487     } else {
45488         // fix randome scrolling
45489         this.el.on('scroll', function() {
45490             Roo.log('fix random scolling');
45491             this.scrollTo('top',0); 
45492         });
45493     }
45494     content = content || this.content;
45495     if(content){
45496         this.setContent(content);
45497     }
45498     if(config && config.url){
45499         this.setUrl(this.url, this.params, this.loadOnce);
45500     }
45501     
45502     
45503     
45504     Roo.ContentPanel.superclass.constructor.call(this);
45505 };
45506
45507 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45508     tabTip:'',
45509     setRegion : function(region){
45510         this.region = region;
45511         if(region){
45512            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45513         }else{
45514            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45515         } 
45516     },
45517     
45518     /**
45519      * Returns the toolbar for this Panel if one was configured. 
45520      * @return {Roo.Toolbar} 
45521      */
45522     getToolbar : function(){
45523         return this.toolbar;
45524     },
45525     
45526     setActiveState : function(active){
45527         this.active = active;
45528         if(!active){
45529             this.fireEvent("deactivate", this);
45530         }else{
45531             this.fireEvent("activate", this);
45532         }
45533     },
45534     /**
45535      * Updates this panel's element
45536      * @param {String} content The new content
45537      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45538     */
45539     setContent : function(content, loadScripts){
45540         this.el.update(content, loadScripts);
45541     },
45542
45543     ignoreResize : function(w, h){
45544         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45545             return true;
45546         }else{
45547             this.lastSize = {width: w, height: h};
45548             return false;
45549         }
45550     },
45551     /**
45552      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45553      * @return {Roo.UpdateManager} The UpdateManager
45554      */
45555     getUpdateManager : function(){
45556         return this.el.getUpdateManager();
45557     },
45558      /**
45559      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45560      * @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:
45561 <pre><code>
45562 panel.load({
45563     url: "your-url.php",
45564     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45565     callback: yourFunction,
45566     scope: yourObject, //(optional scope)
45567     discardUrl: false,
45568     nocache: false,
45569     text: "Loading...",
45570     timeout: 30,
45571     scripts: false
45572 });
45573 </code></pre>
45574      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45575      * 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.
45576      * @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}
45577      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45578      * @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.
45579      * @return {Roo.ContentPanel} this
45580      */
45581     load : function(){
45582         var um = this.el.getUpdateManager();
45583         um.update.apply(um, arguments);
45584         return this;
45585     },
45586
45587
45588     /**
45589      * 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.
45590      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45591      * @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)
45592      * @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)
45593      * @return {Roo.UpdateManager} The UpdateManager
45594      */
45595     setUrl : function(url, params, loadOnce){
45596         if(this.refreshDelegate){
45597             this.removeListener("activate", this.refreshDelegate);
45598         }
45599         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45600         this.on("activate", this.refreshDelegate);
45601         return this.el.getUpdateManager();
45602     },
45603     
45604     _handleRefresh : function(url, params, loadOnce){
45605         if(!loadOnce || !this.loaded){
45606             var updater = this.el.getUpdateManager();
45607             updater.update(url, params, this._setLoaded.createDelegate(this));
45608         }
45609     },
45610     
45611     _setLoaded : function(){
45612         this.loaded = true;
45613     }, 
45614     
45615     /**
45616      * Returns this panel's id
45617      * @return {String} 
45618      */
45619     getId : function(){
45620         return this.el.id;
45621     },
45622     
45623     /** 
45624      * Returns this panel's element - used by regiosn to add.
45625      * @return {Roo.Element} 
45626      */
45627     getEl : function(){
45628         return this.wrapEl || this.el;
45629     },
45630     
45631     adjustForComponents : function(width, height){
45632         if(this.resizeEl != this.el){
45633             width -= this.el.getFrameWidth('lr');
45634             height -= this.el.getFrameWidth('tb');
45635         }
45636         if(this.toolbar){
45637             var te = this.toolbar.getEl();
45638             height -= te.getHeight();
45639             te.setWidth(width);
45640         }
45641         if(this.adjustments){
45642             width += this.adjustments[0];
45643             height += this.adjustments[1];
45644         }
45645         return {"width": width, "height": height};
45646     },
45647     
45648     setSize : function(width, height){
45649         if(this.fitToFrame && !this.ignoreResize(width, height)){
45650             if(this.fitContainer && this.resizeEl != this.el){
45651                 this.el.setSize(width, height);
45652             }
45653             var size = this.adjustForComponents(width, height);
45654             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45655             this.fireEvent('resize', this, size.width, size.height);
45656         }
45657     },
45658     
45659     /**
45660      * Returns this panel's title
45661      * @return {String} 
45662      */
45663     getTitle : function(){
45664         return this.title;
45665     },
45666     
45667     /**
45668      * Set this panel's title
45669      * @param {String} title
45670      */
45671     setTitle : function(title){
45672         this.title = title;
45673         if(this.region){
45674             this.region.updatePanelTitle(this, title);
45675         }
45676     },
45677     
45678     /**
45679      * Returns true is this panel was configured to be closable
45680      * @return {Boolean} 
45681      */
45682     isClosable : function(){
45683         return this.closable;
45684     },
45685     
45686     beforeSlide : function(){
45687         this.el.clip();
45688         this.resizeEl.clip();
45689     },
45690     
45691     afterSlide : function(){
45692         this.el.unclip();
45693         this.resizeEl.unclip();
45694     },
45695     
45696     /**
45697      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45698      *   Will fail silently if the {@link #setUrl} method has not been called.
45699      *   This does not activate the panel, just updates its content.
45700      */
45701     refresh : function(){
45702         if(this.refreshDelegate){
45703            this.loaded = false;
45704            this.refreshDelegate();
45705         }
45706     },
45707     
45708     /**
45709      * Destroys this panel
45710      */
45711     destroy : function(){
45712         this.el.removeAllListeners();
45713         var tempEl = document.createElement("span");
45714         tempEl.appendChild(this.el.dom);
45715         tempEl.innerHTML = "";
45716         this.el.remove();
45717         this.el = null;
45718     },
45719     
45720       /**
45721      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45722      * <pre><code>
45723
45724 layout.addxtype({
45725        xtype : 'Form',
45726        items: [ .... ]
45727    }
45728 );
45729
45730 </code></pre>
45731      * @param {Object} cfg Xtype definition of item to add.
45732      */
45733     
45734     addxtype : function(cfg) {
45735         // add form..
45736         if (cfg.xtype.match(/^Form$/)) {
45737             var el = this.el.createChild();
45738
45739             this.form = new  Roo.form.Form(cfg);
45740             
45741             
45742             if ( this.form.allItems.length) this.form.render(el.dom);
45743             return this.form;
45744         }
45745         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
45746             // views..
45747             cfg.el = this.el.appendChild(document.createElement("div"));
45748             // factory?
45749             var ret = new Roo[cfg.xtype](cfg);
45750             ret.render(false, ''); // render blank..
45751             return ret;
45752             
45753         }
45754         return false;
45755         
45756     }
45757 });
45758
45759 /**
45760  * @class Roo.GridPanel
45761  * @extends Roo.ContentPanel
45762  * @constructor
45763  * Create a new GridPanel.
45764  * @param {Roo.grid.Grid} grid The grid for this panel
45765  * @param {String/Object} config A string to set only the panel's title, or a config object
45766  */
45767 Roo.GridPanel = function(grid, config){
45768     
45769   
45770     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45771         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
45772         
45773     this.wrapper.dom.appendChild(grid.getGridEl().dom);
45774     
45775     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
45776     
45777     if(this.toolbar){
45778         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
45779     }
45780     // xtype created footer. - not sure if will work as we normally have to render first..
45781     if (this.footer && !this.footer.el && this.footer.xtype) {
45782         
45783         this.footer.container = this.grid.getView().getFooterPanel(true);
45784         this.footer.dataSource = this.grid.dataSource;
45785         this.footer = Roo.factory(this.footer, Roo);
45786         
45787     }
45788     
45789     grid.monitorWindowResize = false; // turn off autosizing
45790     grid.autoHeight = false;
45791     grid.autoWidth = false;
45792     this.grid = grid;
45793     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
45794 };
45795
45796 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
45797     getId : function(){
45798         return this.grid.id;
45799     },
45800     
45801     /**
45802      * Returns the grid for this panel
45803      * @return {Roo.grid.Grid} 
45804      */
45805     getGrid : function(){
45806         return this.grid;    
45807     },
45808     
45809     setSize : function(width, height){
45810         if(!this.ignoreResize(width, height)){
45811             var grid = this.grid;
45812             var size = this.adjustForComponents(width, height);
45813             grid.getGridEl().setSize(size.width, size.height);
45814             grid.autoSize();
45815         }
45816     },
45817     
45818     beforeSlide : function(){
45819         this.grid.getView().scroller.clip();
45820     },
45821     
45822     afterSlide : function(){
45823         this.grid.getView().scroller.unclip();
45824     },
45825     
45826     destroy : function(){
45827         this.grid.destroy();
45828         delete this.grid;
45829         Roo.GridPanel.superclass.destroy.call(this); 
45830     }
45831 });
45832
45833
45834 /**
45835  * @class Roo.NestedLayoutPanel
45836  * @extends Roo.ContentPanel
45837  * @constructor
45838  * Create a new NestedLayoutPanel.
45839  * 
45840  * 
45841  * @param {Roo.BorderLayout} layout The layout for this panel
45842  * @param {String/Object} config A string to set only the title or a config object
45843  */
45844 Roo.NestedLayoutPanel = function(layout, config)
45845 {
45846     // construct with only one argument..
45847     /* FIXME - implement nicer consturctors
45848     if (layout.layout) {
45849         config = layout;
45850         layout = config.layout;
45851         delete config.layout;
45852     }
45853     if (layout.xtype && !layout.getEl) {
45854         // then layout needs constructing..
45855         layout = Roo.factory(layout, Roo);
45856     }
45857     */
45858     
45859     
45860     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
45861     
45862     layout.monitorWindowResize = false; // turn off autosizing
45863     this.layout = layout;
45864     this.layout.getEl().addClass("x-layout-nested-layout");
45865     
45866     
45867     
45868     
45869 };
45870
45871 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
45872
45873     setSize : function(width, height){
45874         if(!this.ignoreResize(width, height)){
45875             var size = this.adjustForComponents(width, height);
45876             var el = this.layout.getEl();
45877             el.setSize(size.width, size.height);
45878             var touch = el.dom.offsetWidth;
45879             this.layout.layout();
45880             // ie requires a double layout on the first pass
45881             if(Roo.isIE && !this.initialized){
45882                 this.initialized = true;
45883                 this.layout.layout();
45884             }
45885         }
45886     },
45887     
45888     // activate all subpanels if not currently active..
45889     
45890     setActiveState : function(active){
45891         this.active = active;
45892         if(!active){
45893             this.fireEvent("deactivate", this);
45894             return;
45895         }
45896         
45897         this.fireEvent("activate", this);
45898         // not sure if this should happen before or after..
45899         if (!this.layout) {
45900             return; // should not happen..
45901         }
45902         var reg = false;
45903         for (var r in this.layout.regions) {
45904             reg = this.layout.getRegion(r);
45905             if (reg.getActivePanel()) {
45906                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45907                 reg.setActivePanel(reg.getActivePanel());
45908                 continue;
45909             }
45910             if (!reg.panels.length) {
45911                 continue;
45912             }
45913             reg.showPanel(reg.getPanel(0));
45914         }
45915         
45916         
45917         
45918         
45919     },
45920     
45921     /**
45922      * Returns the nested BorderLayout for this panel
45923      * @return {Roo.BorderLayout} 
45924      */
45925     getLayout : function(){
45926         return this.layout;
45927     },
45928     
45929      /**
45930      * Adds a xtype elements to the layout of the nested panel
45931      * <pre><code>
45932
45933 panel.addxtype({
45934        xtype : 'ContentPanel',
45935        region: 'west',
45936        items: [ .... ]
45937    }
45938 );
45939
45940 panel.addxtype({
45941         xtype : 'NestedLayoutPanel',
45942         region: 'west',
45943         layout: {
45944            center: { },
45945            west: { }   
45946         },
45947         items : [ ... list of content panels or nested layout panels.. ]
45948    }
45949 );
45950 </code></pre>
45951      * @param {Object} cfg Xtype definition of item to add.
45952      */
45953     addxtype : function(cfg) {
45954         return this.layout.addxtype(cfg);
45955     
45956     }
45957 });
45958
45959 Roo.ScrollPanel = function(el, config, content){
45960     config = config || {};
45961     config.fitToFrame = true;
45962     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
45963     
45964     this.el.dom.style.overflow = "hidden";
45965     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
45966     this.el.removeClass("x-layout-inactive-content");
45967     this.el.on("mousewheel", this.onWheel, this);
45968
45969     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
45970     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
45971     up.unselectable(); down.unselectable();
45972     up.on("click", this.scrollUp, this);
45973     down.on("click", this.scrollDown, this);
45974     up.addClassOnOver("x-scroller-btn-over");
45975     down.addClassOnOver("x-scroller-btn-over");
45976     up.addClassOnClick("x-scroller-btn-click");
45977     down.addClassOnClick("x-scroller-btn-click");
45978     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
45979
45980     this.resizeEl = this.el;
45981     this.el = wrap; this.up = up; this.down = down;
45982 };
45983
45984 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
45985     increment : 100,
45986     wheelIncrement : 5,
45987     scrollUp : function(){
45988         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
45989     },
45990
45991     scrollDown : function(){
45992         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
45993     },
45994
45995     afterScroll : function(){
45996         var el = this.resizeEl;
45997         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
45998         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45999         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46000     },
46001
46002     setSize : function(){
46003         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
46004         this.afterScroll();
46005     },
46006
46007     onWheel : function(e){
46008         var d = e.getWheelDelta();
46009         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
46010         this.afterScroll();
46011         e.stopEvent();
46012     },
46013
46014     setContent : function(content, loadScripts){
46015         this.resizeEl.update(content, loadScripts);
46016     }
46017
46018 });
46019
46020
46021
46022
46023
46024
46025
46026
46027
46028 /**
46029  * @class Roo.TreePanel
46030  * @extends Roo.ContentPanel
46031  * @constructor
46032  * Create a new TreePanel. - defaults to fit/scoll contents.
46033  * @param {String/Object} config A string to set only the panel's title, or a config object
46034  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
46035  */
46036 Roo.TreePanel = function(config){
46037     var el = config.el;
46038     var tree = config.tree;
46039     delete config.tree; 
46040     delete config.el; // hopefull!
46041     
46042     // wrapper for IE7 strict & safari scroll issue
46043     
46044     var treeEl = el.createChild();
46045     config.resizeEl = treeEl;
46046     
46047     
46048     
46049     Roo.TreePanel.superclass.constructor.call(this, el, config);
46050  
46051  
46052     this.tree = new Roo.tree.TreePanel(treeEl , tree);
46053     //console.log(tree);
46054     this.on('activate', function()
46055     {
46056         if (this.tree.rendered) {
46057             return;
46058         }
46059         //console.log('render tree');
46060         this.tree.render();
46061     });
46062     
46063     this.on('resize',  function (cp, w, h) {
46064             this.tree.innerCt.setWidth(w);
46065             this.tree.innerCt.setHeight(h);
46066             this.tree.innerCt.setStyle('overflow-y', 'auto');
46067     });
46068
46069         
46070     
46071 };
46072
46073 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
46074     fitToFrame : true,
46075     autoScroll : true
46076 });
46077
46078
46079
46080
46081
46082
46083
46084
46085
46086
46087
46088 /*
46089  * Based on:
46090  * Ext JS Library 1.1.1
46091  * Copyright(c) 2006-2007, Ext JS, LLC.
46092  *
46093  * Originally Released Under LGPL - original licence link has changed is not relivant.
46094  *
46095  * Fork - LGPL
46096  * <script type="text/javascript">
46097  */
46098  
46099
46100 /**
46101  * @class Roo.ReaderLayout
46102  * @extends Roo.BorderLayout
46103  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
46104  * center region containing two nested regions (a top one for a list view and one for item preview below),
46105  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
46106  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
46107  * expedites the setup of the overall layout and regions for this common application style.
46108  * Example:
46109  <pre><code>
46110 var reader = new Roo.ReaderLayout();
46111 var CP = Roo.ContentPanel;  // shortcut for adding
46112
46113 reader.beginUpdate();
46114 reader.add("north", new CP("north", "North"));
46115 reader.add("west", new CP("west", {title: "West"}));
46116 reader.add("east", new CP("east", {title: "East"}));
46117
46118 reader.regions.listView.add(new CP("listView", "List"));
46119 reader.regions.preview.add(new CP("preview", "Preview"));
46120 reader.endUpdate();
46121 </code></pre>
46122 * @constructor
46123 * Create a new ReaderLayout
46124 * @param {Object} config Configuration options
46125 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
46126 * document.body if omitted)
46127 */
46128 Roo.ReaderLayout = function(config, renderTo){
46129     var c = config || {size:{}};
46130     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
46131         north: c.north !== false ? Roo.apply({
46132             split:false,
46133             initialSize: 32,
46134             titlebar: false
46135         }, c.north) : false,
46136         west: c.west !== false ? Roo.apply({
46137             split:true,
46138             initialSize: 200,
46139             minSize: 175,
46140             maxSize: 400,
46141             titlebar: true,
46142             collapsible: true,
46143             animate: true,
46144             margins:{left:5,right:0,bottom:5,top:5},
46145             cmargins:{left:5,right:5,bottom:5,top:5}
46146         }, c.west) : false,
46147         east: c.east !== false ? Roo.apply({
46148             split:true,
46149             initialSize: 200,
46150             minSize: 175,
46151             maxSize: 400,
46152             titlebar: true,
46153             collapsible: true,
46154             animate: true,
46155             margins:{left:0,right:5,bottom:5,top:5},
46156             cmargins:{left:5,right:5,bottom:5,top:5}
46157         }, c.east) : false,
46158         center: Roo.apply({
46159             tabPosition: 'top',
46160             autoScroll:false,
46161             closeOnTab: true,
46162             titlebar:false,
46163             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
46164         }, c.center)
46165     });
46166
46167     this.el.addClass('x-reader');
46168
46169     this.beginUpdate();
46170
46171     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
46172         south: c.preview !== false ? Roo.apply({
46173             split:true,
46174             initialSize: 200,
46175             minSize: 100,
46176             autoScroll:true,
46177             collapsible:true,
46178             titlebar: true,
46179             cmargins:{top:5,left:0, right:0, bottom:0}
46180         }, c.preview) : false,
46181         center: Roo.apply({
46182             autoScroll:false,
46183             titlebar:false,
46184             minHeight:200
46185         }, c.listView)
46186     });
46187     this.add('center', new Roo.NestedLayoutPanel(inner,
46188             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
46189
46190     this.endUpdate();
46191
46192     this.regions.preview = inner.getRegion('south');
46193     this.regions.listView = inner.getRegion('center');
46194 };
46195
46196 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
46197  * Based on:
46198  * Ext JS Library 1.1.1
46199  * Copyright(c) 2006-2007, Ext JS, LLC.
46200  *
46201  * Originally Released Under LGPL - original licence link has changed is not relivant.
46202  *
46203  * Fork - LGPL
46204  * <script type="text/javascript">
46205  */
46206  
46207 /**
46208  * @class Roo.grid.Grid
46209  * @extends Roo.util.Observable
46210  * This class represents the primary interface of a component based grid control.
46211  * <br><br>Usage:<pre><code>
46212  var grid = new Roo.grid.Grid("my-container-id", {
46213      ds: myDataStore,
46214      cm: myColModel,
46215      selModel: mySelectionModel,
46216      autoSizeColumns: true,
46217      monitorWindowResize: false,
46218      trackMouseOver: true
46219  });
46220  // set any options
46221  grid.render();
46222  * </code></pre>
46223  * <b>Common Problems:</b><br/>
46224  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
46225  * element will correct this<br/>
46226  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
46227  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
46228  * are unpredictable.<br/>
46229  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
46230  * grid to calculate dimensions/offsets.<br/>
46231   * @constructor
46232  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
46233  * The container MUST have some type of size defined for the grid to fill. The container will be
46234  * automatically set to position relative if it isn't already.
46235  * @param {Object} config A config object that sets properties on this grid.
46236  */
46237 Roo.grid.Grid = function(container, config){
46238         // initialize the container
46239         this.container = Roo.get(container);
46240         this.container.update("");
46241         this.container.setStyle("overflow", "hidden");
46242     this.container.addClass('x-grid-container');
46243
46244     this.id = this.container.id;
46245
46246     Roo.apply(this, config);
46247     // check and correct shorthanded configs
46248     if(this.ds){
46249         this.dataSource = this.ds;
46250         delete this.ds;
46251     }
46252     if(this.cm){
46253         this.colModel = this.cm;
46254         delete this.cm;
46255     }
46256     if(this.sm){
46257         this.selModel = this.sm;
46258         delete this.sm;
46259     }
46260
46261     if (this.selModel) {
46262         this.selModel = Roo.factory(this.selModel, Roo.grid);
46263         this.sm = this.selModel;
46264         this.sm.xmodule = this.xmodule || false;
46265     }
46266     if (typeof(this.colModel.config) == 'undefined') {
46267         this.colModel = new Roo.grid.ColumnModel(this.colModel);
46268         this.cm = this.colModel;
46269         this.cm.xmodule = this.xmodule || false;
46270     }
46271     if (this.dataSource) {
46272         this.dataSource= Roo.factory(this.dataSource, Roo.data);
46273         this.ds = this.dataSource;
46274         this.ds.xmodule = this.xmodule || false;
46275         
46276     }
46277     
46278     
46279     
46280     if(this.width){
46281         this.container.setWidth(this.width);
46282     }
46283
46284     if(this.height){
46285         this.container.setHeight(this.height);
46286     }
46287     /** @private */
46288         this.addEvents({
46289         // raw events
46290         /**
46291          * @event click
46292          * The raw click event for the entire grid.
46293          * @param {Roo.EventObject} e
46294          */
46295         "click" : true,
46296         /**
46297          * @event dblclick
46298          * The raw dblclick event for the entire grid.
46299          * @param {Roo.EventObject} e
46300          */
46301         "dblclick" : true,
46302         /**
46303          * @event contextmenu
46304          * The raw contextmenu event for the entire grid.
46305          * @param {Roo.EventObject} e
46306          */
46307         "contextmenu" : true,
46308         /**
46309          * @event mousedown
46310          * The raw mousedown event for the entire grid.
46311          * @param {Roo.EventObject} e
46312          */
46313         "mousedown" : true,
46314         /**
46315          * @event mouseup
46316          * The raw mouseup event for the entire grid.
46317          * @param {Roo.EventObject} e
46318          */
46319         "mouseup" : true,
46320         /**
46321          * @event mouseover
46322          * The raw mouseover event for the entire grid.
46323          * @param {Roo.EventObject} e
46324          */
46325         "mouseover" : true,
46326         /**
46327          * @event mouseout
46328          * The raw mouseout event for the entire grid.
46329          * @param {Roo.EventObject} e
46330          */
46331         "mouseout" : true,
46332         /**
46333          * @event keypress
46334          * The raw keypress event for the entire grid.
46335          * @param {Roo.EventObject} e
46336          */
46337         "keypress" : true,
46338         /**
46339          * @event keydown
46340          * The raw keydown event for the entire grid.
46341          * @param {Roo.EventObject} e
46342          */
46343         "keydown" : true,
46344
46345         // custom events
46346
46347         /**
46348          * @event cellclick
46349          * Fires when a cell is clicked
46350          * @param {Grid} this
46351          * @param {Number} rowIndex
46352          * @param {Number} columnIndex
46353          * @param {Roo.EventObject} e
46354          */
46355         "cellclick" : true,
46356         /**
46357          * @event celldblclick
46358          * Fires when a cell is double clicked
46359          * @param {Grid} this
46360          * @param {Number} rowIndex
46361          * @param {Number} columnIndex
46362          * @param {Roo.EventObject} e
46363          */
46364         "celldblclick" : true,
46365         /**
46366          * @event rowclick
46367          * Fires when a row is clicked
46368          * @param {Grid} this
46369          * @param {Number} rowIndex
46370          * @param {Roo.EventObject} e
46371          */
46372         "rowclick" : true,
46373         /**
46374          * @event rowdblclick
46375          * Fires when a row is double clicked
46376          * @param {Grid} this
46377          * @param {Number} rowIndex
46378          * @param {Roo.EventObject} e
46379          */
46380         "rowdblclick" : true,
46381         /**
46382          * @event headerclick
46383          * Fires when a header is clicked
46384          * @param {Grid} this
46385          * @param {Number} columnIndex
46386          * @param {Roo.EventObject} e
46387          */
46388         "headerclick" : true,
46389         /**
46390          * @event headerdblclick
46391          * Fires when a header cell is double clicked
46392          * @param {Grid} this
46393          * @param {Number} columnIndex
46394          * @param {Roo.EventObject} e
46395          */
46396         "headerdblclick" : true,
46397         /**
46398          * @event rowcontextmenu
46399          * Fires when a row is right clicked
46400          * @param {Grid} this
46401          * @param {Number} rowIndex
46402          * @param {Roo.EventObject} e
46403          */
46404         "rowcontextmenu" : true,
46405         /**
46406          * @event cellcontextmenu
46407          * Fires when a cell is right clicked
46408          * @param {Grid} this
46409          * @param {Number} rowIndex
46410          * @param {Number} cellIndex
46411          * @param {Roo.EventObject} e
46412          */
46413          "cellcontextmenu" : true,
46414         /**
46415          * @event headercontextmenu
46416          * Fires when a header is right clicked
46417          * @param {Grid} this
46418          * @param {Number} columnIndex
46419          * @param {Roo.EventObject} e
46420          */
46421         "headercontextmenu" : true,
46422         /**
46423          * @event bodyscroll
46424          * Fires when the body element is scrolled
46425          * @param {Number} scrollLeft
46426          * @param {Number} scrollTop
46427          */
46428         "bodyscroll" : true,
46429         /**
46430          * @event columnresize
46431          * Fires when the user resizes a column
46432          * @param {Number} columnIndex
46433          * @param {Number} newSize
46434          */
46435         "columnresize" : true,
46436         /**
46437          * @event columnmove
46438          * Fires when the user moves a column
46439          * @param {Number} oldIndex
46440          * @param {Number} newIndex
46441          */
46442         "columnmove" : true,
46443         /**
46444          * @event startdrag
46445          * Fires when row(s) start being dragged
46446          * @param {Grid} this
46447          * @param {Roo.GridDD} dd The drag drop object
46448          * @param {event} e The raw browser event
46449          */
46450         "startdrag" : true,
46451         /**
46452          * @event enddrag
46453          * Fires when a drag operation is complete
46454          * @param {Grid} this
46455          * @param {Roo.GridDD} dd The drag drop object
46456          * @param {event} e The raw browser event
46457          */
46458         "enddrag" : true,
46459         /**
46460          * @event dragdrop
46461          * Fires when dragged row(s) are dropped on a valid DD target
46462          * @param {Grid} this
46463          * @param {Roo.GridDD} dd The drag drop object
46464          * @param {String} targetId The target drag drop object
46465          * @param {event} e The raw browser event
46466          */
46467         "dragdrop" : true,
46468         /**
46469          * @event dragover
46470          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
46471          * @param {Grid} this
46472          * @param {Roo.GridDD} dd The drag drop object
46473          * @param {String} targetId The target drag drop object
46474          * @param {event} e The raw browser event
46475          */
46476         "dragover" : true,
46477         /**
46478          * @event dragenter
46479          *  Fires when the dragged row(s) first cross another DD target while being dragged
46480          * @param {Grid} this
46481          * @param {Roo.GridDD} dd The drag drop object
46482          * @param {String} targetId The target drag drop object
46483          * @param {event} e The raw browser event
46484          */
46485         "dragenter" : true,
46486         /**
46487          * @event dragout
46488          * Fires when the dragged row(s) leave another DD target while being dragged
46489          * @param {Grid} this
46490          * @param {Roo.GridDD} dd The drag drop object
46491          * @param {String} targetId The target drag drop object
46492          * @param {event} e The raw browser event
46493          */
46494         "dragout" : true,
46495         /**
46496          * @event rowclass
46497          * Fires when a row is rendered, so you can change add a style to it.
46498          * @param {GridView} gridview   The grid view
46499          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
46500          */
46501         'rowclass' : true,
46502
46503         /**
46504          * @event render
46505          * Fires when the grid is rendered
46506          * @param {Grid} grid
46507          */
46508         'render' : true
46509     });
46510
46511     Roo.grid.Grid.superclass.constructor.call(this);
46512 };
46513 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46514     
46515     /**
46516      * @cfg {String} ddGroup - drag drop group.
46517      */
46518
46519     /**
46520      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46521      */
46522     minColumnWidth : 25,
46523
46524     /**
46525      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46526      * <b>on initial render.</b> It is more efficient to explicitly size the columns
46527      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46528      */
46529     autoSizeColumns : false,
46530
46531     /**
46532      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46533      */
46534     autoSizeHeaders : true,
46535
46536     /**
46537      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46538      */
46539     monitorWindowResize : true,
46540
46541     /**
46542      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46543      * rows measured to get a columns size. Default is 0 (all rows).
46544      */
46545     maxRowsToMeasure : 0,
46546
46547     /**
46548      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46549      */
46550     trackMouseOver : true,
46551
46552     /**
46553     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46554     */
46555     
46556     /**
46557     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46558     */
46559     enableDragDrop : false,
46560     
46561     /**
46562     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46563     */
46564     enableColumnMove : true,
46565     
46566     /**
46567     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46568     */
46569     enableColumnHide : true,
46570     
46571     /**
46572     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46573     */
46574     enableRowHeightSync : false,
46575     
46576     /**
46577     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46578     */
46579     stripeRows : true,
46580     
46581     /**
46582     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46583     */
46584     autoHeight : false,
46585
46586     /**
46587      * @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.
46588      */
46589     autoExpandColumn : false,
46590
46591     /**
46592     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46593     * Default is 50.
46594     */
46595     autoExpandMin : 50,
46596
46597     /**
46598     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46599     */
46600     autoExpandMax : 1000,
46601
46602     /**
46603     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46604     */
46605     view : null,
46606
46607     /**
46608     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46609     */
46610     loadMask : false,
46611     /**
46612     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
46613     */
46614     dropTarget: false,
46615     
46616     // private
46617     rendered : false,
46618
46619     /**
46620     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46621     * of a fixed width. Default is false.
46622     */
46623     /**
46624     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46625     */
46626     /**
46627      * Called once after all setup has been completed and the grid is ready to be rendered.
46628      * @return {Roo.grid.Grid} this
46629      */
46630     render : function()
46631     {
46632         var c = this.container;
46633         // try to detect autoHeight/width mode
46634         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46635             this.autoHeight = true;
46636         }
46637         var view = this.getView();
46638         view.init(this);
46639
46640         c.on("click", this.onClick, this);
46641         c.on("dblclick", this.onDblClick, this);
46642         c.on("contextmenu", this.onContextMenu, this);
46643         c.on("keydown", this.onKeyDown, this);
46644
46645         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46646
46647         this.getSelectionModel().init(this);
46648
46649         view.render();
46650
46651         if(this.loadMask){
46652             this.loadMask = new Roo.LoadMask(this.container,
46653                     Roo.apply({store:this.dataSource}, this.loadMask));
46654         }
46655         
46656         
46657         if (this.toolbar && this.toolbar.xtype) {
46658             this.toolbar.container = this.getView().getHeaderPanel(true);
46659             this.toolbar = new Ext.Toolbar(this.toolbar);
46660         }
46661         if (this.footer && this.footer.xtype) {
46662             this.footer.dataSource = this.getDataSource();
46663             this.footer.container = this.getView().getFooterPanel(true);
46664             this.footer = Roo.factory(this.footer, Roo);
46665         }
46666         if (this.dropTarget && this.dropTarget.xtype) {
46667             delete this.dropTarget.xtype;
46668             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
46669         }
46670         
46671         
46672         this.rendered = true;
46673         this.fireEvent('render', this);
46674         return this;
46675     },
46676
46677         /**
46678          * Reconfigures the grid to use a different Store and Column Model.
46679          * The View will be bound to the new objects and refreshed.
46680          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46681          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46682          */
46683     reconfigure : function(dataSource, colModel){
46684         if(this.loadMask){
46685             this.loadMask.destroy();
46686             this.loadMask = new Roo.LoadMask(this.container,
46687                     Roo.apply({store:dataSource}, this.loadMask));
46688         }
46689         this.view.bind(dataSource, colModel);
46690         this.dataSource = dataSource;
46691         this.colModel = colModel;
46692         this.view.refresh(true);
46693     },
46694
46695     // private
46696     onKeyDown : function(e){
46697         this.fireEvent("keydown", e);
46698     },
46699
46700     /**
46701      * Destroy this grid.
46702      * @param {Boolean} removeEl True to remove the element
46703      */
46704     destroy : function(removeEl, keepListeners){
46705         if(this.loadMask){
46706             this.loadMask.destroy();
46707         }
46708         var c = this.container;
46709         c.removeAllListeners();
46710         this.view.destroy();
46711         this.colModel.purgeListeners();
46712         if(!keepListeners){
46713             this.purgeListeners();
46714         }
46715         c.update("");
46716         if(removeEl === true){
46717             c.remove();
46718         }
46719     },
46720
46721     // private
46722     processEvent : function(name, e){
46723         this.fireEvent(name, e);
46724         var t = e.getTarget();
46725         var v = this.view;
46726         var header = v.findHeaderIndex(t);
46727         if(header !== false){
46728             this.fireEvent("header" + name, this, header, e);
46729         }else{
46730             var row = v.findRowIndex(t);
46731             var cell = v.findCellIndex(t);
46732             if(row !== false){
46733                 this.fireEvent("row" + name, this, row, e);
46734                 if(cell !== false){
46735                     this.fireEvent("cell" + name, this, row, cell, e);
46736                 }
46737             }
46738         }
46739     },
46740
46741     // private
46742     onClick : function(e){
46743         this.processEvent("click", e);
46744     },
46745
46746     // private
46747     onContextMenu : function(e, t){
46748         this.processEvent("contextmenu", e);
46749     },
46750
46751     // private
46752     onDblClick : function(e){
46753         this.processEvent("dblclick", e);
46754     },
46755
46756     // private
46757     walkCells : function(row, col, step, fn, scope){
46758         var cm = this.colModel, clen = cm.getColumnCount();
46759         var ds = this.dataSource, rlen = ds.getCount(), first = true;
46760         if(step < 0){
46761             if(col < 0){
46762                 row--;
46763                 first = false;
46764             }
46765             while(row >= 0){
46766                 if(!first){
46767                     col = clen-1;
46768                 }
46769                 first = false;
46770                 while(col >= 0){
46771                     if(fn.call(scope || this, row, col, cm) === true){
46772                         return [row, col];
46773                     }
46774                     col--;
46775                 }
46776                 row--;
46777             }
46778         } else {
46779             if(col >= clen){
46780                 row++;
46781                 first = false;
46782             }
46783             while(row < rlen){
46784                 if(!first){
46785                     col = 0;
46786                 }
46787                 first = false;
46788                 while(col < clen){
46789                     if(fn.call(scope || this, row, col, cm) === true){
46790                         return [row, col];
46791                     }
46792                     col++;
46793                 }
46794                 row++;
46795             }
46796         }
46797         return null;
46798     },
46799
46800     // private
46801     getSelections : function(){
46802         return this.selModel.getSelections();
46803     },
46804
46805     /**
46806      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
46807      * but if manual update is required this method will initiate it.
46808      */
46809     autoSize : function(){
46810         if(this.rendered){
46811             this.view.layout();
46812             if(this.view.adjustForScroll){
46813                 this.view.adjustForScroll();
46814             }
46815         }
46816     },
46817
46818     /**
46819      * Returns the grid's underlying element.
46820      * @return {Element} The element
46821      */
46822     getGridEl : function(){
46823         return this.container;
46824     },
46825
46826     // private for compatibility, overridden by editor grid
46827     stopEditing : function(){},
46828
46829     /**
46830      * Returns the grid's SelectionModel.
46831      * @return {SelectionModel}
46832      */
46833     getSelectionModel : function(){
46834         if(!this.selModel){
46835             this.selModel = new Roo.grid.RowSelectionModel();
46836         }
46837         return this.selModel;
46838     },
46839
46840     /**
46841      * Returns the grid's DataSource.
46842      * @return {DataSource}
46843      */
46844     getDataSource : function(){
46845         return this.dataSource;
46846     },
46847
46848     /**
46849      * Returns the grid's ColumnModel.
46850      * @return {ColumnModel}
46851      */
46852     getColumnModel : function(){
46853         return this.colModel;
46854     },
46855
46856     /**
46857      * Returns the grid's GridView object.
46858      * @return {GridView}
46859      */
46860     getView : function(){
46861         if(!this.view){
46862             this.view = new Roo.grid.GridView(this.viewConfig);
46863         }
46864         return this.view;
46865     },
46866     /**
46867      * Called to get grid's drag proxy text, by default returns this.ddText.
46868      * @return {String}
46869      */
46870     getDragDropText : function(){
46871         var count = this.selModel.getCount();
46872         return String.format(this.ddText, count, count == 1 ? '' : 's');
46873     }
46874 });
46875 /**
46876  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
46877  * %0 is replaced with the number of selected rows.
46878  * @type String
46879  */
46880 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
46881  * Based on:
46882  * Ext JS Library 1.1.1
46883  * Copyright(c) 2006-2007, Ext JS, LLC.
46884  *
46885  * Originally Released Under LGPL - original licence link has changed is not relivant.
46886  *
46887  * Fork - LGPL
46888  * <script type="text/javascript">
46889  */
46890  
46891 Roo.grid.AbstractGridView = function(){
46892         this.grid = null;
46893         
46894         this.events = {
46895             "beforerowremoved" : true,
46896             "beforerowsinserted" : true,
46897             "beforerefresh" : true,
46898             "rowremoved" : true,
46899             "rowsinserted" : true,
46900             "rowupdated" : true,
46901             "refresh" : true
46902         };
46903     Roo.grid.AbstractGridView.superclass.constructor.call(this);
46904 };
46905
46906 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
46907     rowClass : "x-grid-row",
46908     cellClass : "x-grid-cell",
46909     tdClass : "x-grid-td",
46910     hdClass : "x-grid-hd",
46911     splitClass : "x-grid-hd-split",
46912     
46913         init: function(grid){
46914         this.grid = grid;
46915                 var cid = this.grid.getGridEl().id;
46916         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
46917         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
46918         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
46919         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
46920         },
46921         
46922         getColumnRenderers : function(){
46923         var renderers = [];
46924         var cm = this.grid.colModel;
46925         var colCount = cm.getColumnCount();
46926         for(var i = 0; i < colCount; i++){
46927             renderers[i] = cm.getRenderer(i);
46928         }
46929         return renderers;
46930     },
46931     
46932     getColumnIds : function(){
46933         var ids = [];
46934         var cm = this.grid.colModel;
46935         var colCount = cm.getColumnCount();
46936         for(var i = 0; i < colCount; i++){
46937             ids[i] = cm.getColumnId(i);
46938         }
46939         return ids;
46940     },
46941     
46942     getDataIndexes : function(){
46943         if(!this.indexMap){
46944             this.indexMap = this.buildIndexMap();
46945         }
46946         return this.indexMap.colToData;
46947     },
46948     
46949     getColumnIndexByDataIndex : function(dataIndex){
46950         if(!this.indexMap){
46951             this.indexMap = this.buildIndexMap();
46952         }
46953         return this.indexMap.dataToCol[dataIndex];
46954     },
46955     
46956     /**
46957      * Set a css style for a column dynamically. 
46958      * @param {Number} colIndex The index of the column
46959      * @param {String} name The css property name
46960      * @param {String} value The css value
46961      */
46962     setCSSStyle : function(colIndex, name, value){
46963         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
46964         Roo.util.CSS.updateRule(selector, name, value);
46965     },
46966     
46967     generateRules : function(cm){
46968         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
46969         Roo.util.CSS.removeStyleSheet(rulesId);
46970         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46971             var cid = cm.getColumnId(i);
46972             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
46973                          this.tdSelector, cid, " {\n}\n",
46974                          this.hdSelector, cid, " {\n}\n",
46975                          this.splitSelector, cid, " {\n}\n");
46976         }
46977         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46978     }
46979 });/*
46980  * Based on:
46981  * Ext JS Library 1.1.1
46982  * Copyright(c) 2006-2007, Ext JS, LLC.
46983  *
46984  * Originally Released Under LGPL - original licence link has changed is not relivant.
46985  *
46986  * Fork - LGPL
46987  * <script type="text/javascript">
46988  */
46989
46990 // private
46991 // This is a support class used internally by the Grid components
46992 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
46993     this.grid = grid;
46994     this.view = grid.getView();
46995     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46996     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
46997     if(hd2){
46998         this.setHandleElId(Roo.id(hd));
46999         this.setOuterHandleElId(Roo.id(hd2));
47000     }
47001     this.scroll = false;
47002 };
47003 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
47004     maxDragWidth: 120,
47005     getDragData : function(e){
47006         var t = Roo.lib.Event.getTarget(e);
47007         var h = this.view.findHeaderCell(t);
47008         if(h){
47009             return {ddel: h.firstChild, header:h};
47010         }
47011         return false;
47012     },
47013
47014     onInitDrag : function(e){
47015         this.view.headersDisabled = true;
47016         var clone = this.dragData.ddel.cloneNode(true);
47017         clone.id = Roo.id();
47018         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
47019         this.proxy.update(clone);
47020         return true;
47021     },
47022
47023     afterValidDrop : function(){
47024         var v = this.view;
47025         setTimeout(function(){
47026             v.headersDisabled = false;
47027         }, 50);
47028     },
47029
47030     afterInvalidDrop : function(){
47031         var v = this.view;
47032         setTimeout(function(){
47033             v.headersDisabled = false;
47034         }, 50);
47035     }
47036 });
47037 /*
47038  * Based on:
47039  * Ext JS Library 1.1.1
47040  * Copyright(c) 2006-2007, Ext JS, LLC.
47041  *
47042  * Originally Released Under LGPL - original licence link has changed is not relivant.
47043  *
47044  * Fork - LGPL
47045  * <script type="text/javascript">
47046  */
47047 // private
47048 // This is a support class used internally by the Grid components
47049 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
47050     this.grid = grid;
47051     this.view = grid.getView();
47052     // split the proxies so they don't interfere with mouse events
47053     this.proxyTop = Roo.DomHelper.append(document.body, {
47054         cls:"col-move-top", html:"&#160;"
47055     }, true);
47056     this.proxyBottom = Roo.DomHelper.append(document.body, {
47057         cls:"col-move-bottom", html:"&#160;"
47058     }, true);
47059     this.proxyTop.hide = this.proxyBottom.hide = function(){
47060         this.setLeftTop(-100,-100);
47061         this.setStyle("visibility", "hidden");
47062     };
47063     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47064     // temporarily disabled
47065     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
47066     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
47067 };
47068 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
47069     proxyOffsets : [-4, -9],
47070     fly: Roo.Element.fly,
47071
47072     getTargetFromEvent : function(e){
47073         var t = Roo.lib.Event.getTarget(e);
47074         var cindex = this.view.findCellIndex(t);
47075         if(cindex !== false){
47076             return this.view.getHeaderCell(cindex);
47077         }
47078     },
47079
47080     nextVisible : function(h){
47081         var v = this.view, cm = this.grid.colModel;
47082         h = h.nextSibling;
47083         while(h){
47084             if(!cm.isHidden(v.getCellIndex(h))){
47085                 return h;
47086             }
47087             h = h.nextSibling;
47088         }
47089         return null;
47090     },
47091
47092     prevVisible : function(h){
47093         var v = this.view, cm = this.grid.colModel;
47094         h = h.prevSibling;
47095         while(h){
47096             if(!cm.isHidden(v.getCellIndex(h))){
47097                 return h;
47098             }
47099             h = h.prevSibling;
47100         }
47101         return null;
47102     },
47103
47104     positionIndicator : function(h, n, e){
47105         var x = Roo.lib.Event.getPageX(e);
47106         var r = Roo.lib.Dom.getRegion(n.firstChild);
47107         var px, pt, py = r.top + this.proxyOffsets[1];
47108         if((r.right - x) <= (r.right-r.left)/2){
47109             px = r.right+this.view.borderWidth;
47110             pt = "after";
47111         }else{
47112             px = r.left;
47113             pt = "before";
47114         }
47115         var oldIndex = this.view.getCellIndex(h);
47116         var newIndex = this.view.getCellIndex(n);
47117
47118         if(this.grid.colModel.isFixed(newIndex)){
47119             return false;
47120         }
47121
47122         var locked = this.grid.colModel.isLocked(newIndex);
47123
47124         if(pt == "after"){
47125             newIndex++;
47126         }
47127         if(oldIndex < newIndex){
47128             newIndex--;
47129         }
47130         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
47131             return false;
47132         }
47133         px +=  this.proxyOffsets[0];
47134         this.proxyTop.setLeftTop(px, py);
47135         this.proxyTop.show();
47136         if(!this.bottomOffset){
47137             this.bottomOffset = this.view.mainHd.getHeight();
47138         }
47139         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
47140         this.proxyBottom.show();
47141         return pt;
47142     },
47143
47144     onNodeEnter : function(n, dd, e, data){
47145         if(data.header != n){
47146             this.positionIndicator(data.header, n, e);
47147         }
47148     },
47149
47150     onNodeOver : function(n, dd, e, data){
47151         var result = false;
47152         if(data.header != n){
47153             result = this.positionIndicator(data.header, n, e);
47154         }
47155         if(!result){
47156             this.proxyTop.hide();
47157             this.proxyBottom.hide();
47158         }
47159         return result ? this.dropAllowed : this.dropNotAllowed;
47160     },
47161
47162     onNodeOut : function(n, dd, e, data){
47163         this.proxyTop.hide();
47164         this.proxyBottom.hide();
47165     },
47166
47167     onNodeDrop : function(n, dd, e, data){
47168         var h = data.header;
47169         if(h != n){
47170             var cm = this.grid.colModel;
47171             var x = Roo.lib.Event.getPageX(e);
47172             var r = Roo.lib.Dom.getRegion(n.firstChild);
47173             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
47174             var oldIndex = this.view.getCellIndex(h);
47175             var newIndex = this.view.getCellIndex(n);
47176             var locked = cm.isLocked(newIndex);
47177             if(pt == "after"){
47178                 newIndex++;
47179             }
47180             if(oldIndex < newIndex){
47181                 newIndex--;
47182             }
47183             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
47184                 return false;
47185             }
47186             cm.setLocked(oldIndex, locked, true);
47187             cm.moveColumn(oldIndex, newIndex);
47188             this.grid.fireEvent("columnmove", oldIndex, newIndex);
47189             return true;
47190         }
47191         return false;
47192     }
47193 });
47194 /*
47195  * Based on:
47196  * Ext JS Library 1.1.1
47197  * Copyright(c) 2006-2007, Ext JS, LLC.
47198  *
47199  * Originally Released Under LGPL - original licence link has changed is not relivant.
47200  *
47201  * Fork - LGPL
47202  * <script type="text/javascript">
47203  */
47204   
47205 /**
47206  * @class Roo.grid.GridView
47207  * @extends Roo.util.Observable
47208  *
47209  * @constructor
47210  * @param {Object} config
47211  */
47212 Roo.grid.GridView = function(config){
47213     Roo.grid.GridView.superclass.constructor.call(this);
47214     this.el = null;
47215
47216     Roo.apply(this, config);
47217 };
47218
47219 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
47220
47221     /**
47222      * Override this function to apply custom css classes to rows during rendering
47223      * @param {Record} record The record
47224      * @param {Number} index
47225      * @method getRowClass
47226      */
47227     rowClass : "x-grid-row",
47228
47229     cellClass : "x-grid-col",
47230
47231     tdClass : "x-grid-td",
47232
47233     hdClass : "x-grid-hd",
47234
47235     splitClass : "x-grid-split",
47236
47237     sortClasses : ["sort-asc", "sort-desc"],
47238
47239     enableMoveAnim : false,
47240
47241     hlColor: "C3DAF9",
47242
47243     dh : Roo.DomHelper,
47244
47245     fly : Roo.Element.fly,
47246
47247     css : Roo.util.CSS,
47248
47249     borderWidth: 1,
47250
47251     splitOffset: 3,
47252
47253     scrollIncrement : 22,
47254
47255     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
47256
47257     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
47258
47259     bind : function(ds, cm){
47260         if(this.ds){
47261             this.ds.un("load", this.onLoad, this);
47262             this.ds.un("datachanged", this.onDataChange, this);
47263             this.ds.un("add", this.onAdd, this);
47264             this.ds.un("remove", this.onRemove, this);
47265             this.ds.un("update", this.onUpdate, this);
47266             this.ds.un("clear", this.onClear, this);
47267         }
47268         if(ds){
47269             ds.on("load", this.onLoad, this);
47270             ds.on("datachanged", this.onDataChange, this);
47271             ds.on("add", this.onAdd, this);
47272             ds.on("remove", this.onRemove, this);
47273             ds.on("update", this.onUpdate, this);
47274             ds.on("clear", this.onClear, this);
47275         }
47276         this.ds = ds;
47277
47278         if(this.cm){
47279             this.cm.un("widthchange", this.onColWidthChange, this);
47280             this.cm.un("headerchange", this.onHeaderChange, this);
47281             this.cm.un("hiddenchange", this.onHiddenChange, this);
47282             this.cm.un("columnmoved", this.onColumnMove, this);
47283             this.cm.un("columnlockchange", this.onColumnLock, this);
47284         }
47285         if(cm){
47286             this.generateRules(cm);
47287             cm.on("widthchange", this.onColWidthChange, this);
47288             cm.on("headerchange", this.onHeaderChange, this);
47289             cm.on("hiddenchange", this.onHiddenChange, this);
47290             cm.on("columnmoved", this.onColumnMove, this);
47291             cm.on("columnlockchange", this.onColumnLock, this);
47292         }
47293         this.cm = cm;
47294     },
47295
47296     init: function(grid){
47297         Roo.grid.GridView.superclass.init.call(this, grid);
47298
47299         this.bind(grid.dataSource, grid.colModel);
47300
47301         grid.on("headerclick", this.handleHeaderClick, this);
47302
47303         if(grid.trackMouseOver){
47304             grid.on("mouseover", this.onRowOver, this);
47305             grid.on("mouseout", this.onRowOut, this);
47306         }
47307         grid.cancelTextSelection = function(){};
47308         this.gridId = grid.id;
47309
47310         var tpls = this.templates || {};
47311
47312         if(!tpls.master){
47313             tpls.master = new Roo.Template(
47314                '<div class="x-grid" hidefocus="true">',
47315                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
47316                   '<div class="x-grid-topbar"></div>',
47317                   '<div class="x-grid-scroller"><div></div></div>',
47318                   '<div class="x-grid-locked">',
47319                       '<div class="x-grid-header">{lockedHeader}</div>',
47320                       '<div class="x-grid-body">{lockedBody}</div>',
47321                   "</div>",
47322                   '<div class="x-grid-viewport">',
47323                       '<div class="x-grid-header">{header}</div>',
47324                       '<div class="x-grid-body">{body}</div>',
47325                   "</div>",
47326                   '<div class="x-grid-bottombar"></div>',
47327                  
47328                   '<div class="x-grid-resize-proxy">&#160;</div>',
47329                "</div>"
47330             );
47331             tpls.master.disableformats = true;
47332         }
47333
47334         if(!tpls.header){
47335             tpls.header = new Roo.Template(
47336                '<table border="0" cellspacing="0" cellpadding="0">',
47337                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
47338                "</table>{splits}"
47339             );
47340             tpls.header.disableformats = true;
47341         }
47342         tpls.header.compile();
47343
47344         if(!tpls.hcell){
47345             tpls.hcell = new Roo.Template(
47346                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
47347                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
47348                 "</div></td>"
47349              );
47350              tpls.hcell.disableFormats = true;
47351         }
47352         tpls.hcell.compile();
47353
47354         if(!tpls.hsplit){
47355             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47356             tpls.hsplit.disableFormats = true;
47357         }
47358         tpls.hsplit.compile();
47359
47360         if(!tpls.body){
47361             tpls.body = new Roo.Template(
47362                '<table border="0" cellspacing="0" cellpadding="0">',
47363                "<tbody>{rows}</tbody>",
47364                "</table>"
47365             );
47366             tpls.body.disableFormats = true;
47367         }
47368         tpls.body.compile();
47369
47370         if(!tpls.row){
47371             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47372             tpls.row.disableFormats = true;
47373         }
47374         tpls.row.compile();
47375
47376         if(!tpls.cell){
47377             tpls.cell = new Roo.Template(
47378                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47379                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47380                 "</td>"
47381             );
47382             tpls.cell.disableFormats = true;
47383         }
47384         tpls.cell.compile();
47385
47386         this.templates = tpls;
47387     },
47388
47389     // remap these for backwards compat
47390     onColWidthChange : function(){
47391         this.updateColumns.apply(this, arguments);
47392     },
47393     onHeaderChange : function(){
47394         this.updateHeaders.apply(this, arguments);
47395     }, 
47396     onHiddenChange : function(){
47397         this.handleHiddenChange.apply(this, arguments);
47398     },
47399     onColumnMove : function(){
47400         this.handleColumnMove.apply(this, arguments);
47401     },
47402     onColumnLock : function(){
47403         this.handleLockChange.apply(this, arguments);
47404     },
47405
47406     onDataChange : function(){
47407         this.refresh();
47408         this.updateHeaderSortState();
47409     },
47410
47411     onClear : function(){
47412         this.refresh();
47413     },
47414
47415     onUpdate : function(ds, record){
47416         this.refreshRow(record);
47417     },
47418
47419     refreshRow : function(record){
47420         var ds = this.ds, index;
47421         if(typeof record == 'number'){
47422             index = record;
47423             record = ds.getAt(index);
47424         }else{
47425             index = ds.indexOf(record);
47426         }
47427         this.insertRows(ds, index, index, true);
47428         this.onRemove(ds, record, index+1, true);
47429         this.syncRowHeights(index, index);
47430         this.layout();
47431         this.fireEvent("rowupdated", this, index, record);
47432     },
47433
47434     onAdd : function(ds, records, index){
47435         this.insertRows(ds, index, index + (records.length-1));
47436     },
47437
47438     onRemove : function(ds, record, index, isUpdate){
47439         if(isUpdate !== true){
47440             this.fireEvent("beforerowremoved", this, index, record);
47441         }
47442         var bt = this.getBodyTable(), lt = this.getLockedTable();
47443         if(bt.rows[index]){
47444             bt.firstChild.removeChild(bt.rows[index]);
47445         }
47446         if(lt.rows[index]){
47447             lt.firstChild.removeChild(lt.rows[index]);
47448         }
47449         if(isUpdate !== true){
47450             this.stripeRows(index);
47451             this.syncRowHeights(index, index);
47452             this.layout();
47453             this.fireEvent("rowremoved", this, index, record);
47454         }
47455     },
47456
47457     onLoad : function(){
47458         this.scrollToTop();
47459     },
47460
47461     /**
47462      * Scrolls the grid to the top
47463      */
47464     scrollToTop : function(){
47465         if(this.scroller){
47466             this.scroller.dom.scrollTop = 0;
47467             this.syncScroll();
47468         }
47469     },
47470
47471     /**
47472      * Gets a panel in the header of the grid that can be used for toolbars etc.
47473      * After modifying the contents of this panel a call to grid.autoSize() may be
47474      * required to register any changes in size.
47475      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47476      * @return Roo.Element
47477      */
47478     getHeaderPanel : function(doShow){
47479         if(doShow){
47480             this.headerPanel.show();
47481         }
47482         return this.headerPanel;
47483     },
47484
47485     /**
47486      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47487      * After modifying the contents of this panel a call to grid.autoSize() may be
47488      * required to register any changes in size.
47489      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47490      * @return Roo.Element
47491      */
47492     getFooterPanel : function(doShow){
47493         if(doShow){
47494             this.footerPanel.show();
47495         }
47496         return this.footerPanel;
47497     },
47498
47499     initElements : function(){
47500         var E = Roo.Element;
47501         var el = this.grid.getGridEl().dom.firstChild;
47502         var cs = el.childNodes;
47503
47504         this.el = new E(el);
47505         
47506          this.focusEl = new E(el.firstChild);
47507         this.focusEl.swallowEvent("click", true);
47508         
47509         this.headerPanel = new E(cs[1]);
47510         this.headerPanel.enableDisplayMode("block");
47511
47512         this.scroller = new E(cs[2]);
47513         this.scrollSizer = new E(this.scroller.dom.firstChild);
47514
47515         this.lockedWrap = new E(cs[3]);
47516         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47517         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47518
47519         this.mainWrap = new E(cs[4]);
47520         this.mainHd = new E(this.mainWrap.dom.firstChild);
47521         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47522
47523         this.footerPanel = new E(cs[5]);
47524         this.footerPanel.enableDisplayMode("block");
47525
47526         this.resizeProxy = new E(cs[6]);
47527
47528         this.headerSelector = String.format(
47529            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47530            this.lockedHd.id, this.mainHd.id
47531         );
47532
47533         this.splitterSelector = String.format(
47534            '#{0} div.x-grid-split, #{1} div.x-grid-split',
47535            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47536         );
47537     },
47538     idToCssName : function(s)
47539     {
47540         return s.replace(/[^a-z0-9]+/ig, '-');
47541     },
47542
47543     getHeaderCell : function(index){
47544         return Roo.DomQuery.select(this.headerSelector)[index];
47545     },
47546
47547     getHeaderCellMeasure : function(index){
47548         return this.getHeaderCell(index).firstChild;
47549     },
47550
47551     getHeaderCellText : function(index){
47552         return this.getHeaderCell(index).firstChild.firstChild;
47553     },
47554
47555     getLockedTable : function(){
47556         return this.lockedBody.dom.firstChild;
47557     },
47558
47559     getBodyTable : function(){
47560         return this.mainBody.dom.firstChild;
47561     },
47562
47563     getLockedRow : function(index){
47564         return this.getLockedTable().rows[index];
47565     },
47566
47567     getRow : function(index){
47568         return this.getBodyTable().rows[index];
47569     },
47570
47571     getRowComposite : function(index){
47572         if(!this.rowEl){
47573             this.rowEl = new Roo.CompositeElementLite();
47574         }
47575         var els = [], lrow, mrow;
47576         if(lrow = this.getLockedRow(index)){
47577             els.push(lrow);
47578         }
47579         if(mrow = this.getRow(index)){
47580             els.push(mrow);
47581         }
47582         this.rowEl.elements = els;
47583         return this.rowEl;
47584     },
47585
47586     getCell : function(rowIndex, colIndex){
47587         var locked = this.cm.getLockedCount();
47588         var source;
47589         if(colIndex < locked){
47590             source = this.lockedBody.dom.firstChild;
47591         }else{
47592             source = this.mainBody.dom.firstChild;
47593             colIndex -= locked;
47594         }
47595         return source.rows[rowIndex].childNodes[colIndex];
47596     },
47597
47598     getCellText : function(rowIndex, colIndex){
47599         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47600     },
47601
47602     getCellBox : function(cell){
47603         var b = this.fly(cell).getBox();
47604         if(Roo.isOpera){ // opera fails to report the Y
47605             b.y = cell.offsetTop + this.mainBody.getY();
47606         }
47607         return b;
47608     },
47609
47610     getCellIndex : function(cell){
47611         var id = String(cell.className).match(this.cellRE);
47612         if(id){
47613             return parseInt(id[1], 10);
47614         }
47615         return 0;
47616     },
47617
47618     findHeaderIndex : function(n){
47619         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47620         return r ? this.getCellIndex(r) : false;
47621     },
47622
47623     findHeaderCell : function(n){
47624         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47625         return r ? r : false;
47626     },
47627
47628     findRowIndex : function(n){
47629         if(!n){
47630             return false;
47631         }
47632         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47633         return r ? r.rowIndex : false;
47634     },
47635
47636     findCellIndex : function(node){
47637         var stop = this.el.dom;
47638         while(node && node != stop){
47639             if(this.findRE.test(node.className)){
47640                 return this.getCellIndex(node);
47641             }
47642             node = node.parentNode;
47643         }
47644         return false;
47645     },
47646
47647     getColumnId : function(index){
47648         return this.cm.getColumnId(index);
47649     },
47650
47651     getSplitters : function()
47652     {
47653         if(this.splitterSelector){
47654            return Roo.DomQuery.select(this.splitterSelector);
47655         }else{
47656             return null;
47657       }
47658     },
47659
47660     getSplitter : function(index){
47661         return this.getSplitters()[index];
47662     },
47663
47664     onRowOver : function(e, t){
47665         var row;
47666         if((row = this.findRowIndex(t)) !== false){
47667             this.getRowComposite(row).addClass("x-grid-row-over");
47668         }
47669     },
47670
47671     onRowOut : function(e, t){
47672         var row;
47673         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47674             this.getRowComposite(row).removeClass("x-grid-row-over");
47675         }
47676     },
47677
47678     renderHeaders : function(){
47679         var cm = this.cm;
47680         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47681         var cb = [], lb = [], sb = [], lsb = [], p = {};
47682         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47683             p.cellId = "x-grid-hd-0-" + i;
47684             p.splitId = "x-grid-csplit-0-" + i;
47685             p.id = cm.getColumnId(i);
47686             p.title = cm.getColumnTooltip(i) || "";
47687             p.value = cm.getColumnHeader(i) || "";
47688             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47689             if(!cm.isLocked(i)){
47690                 cb[cb.length] = ct.apply(p);
47691                 sb[sb.length] = st.apply(p);
47692             }else{
47693                 lb[lb.length] = ct.apply(p);
47694                 lsb[lsb.length] = st.apply(p);
47695             }
47696         }
47697         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47698                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47699     },
47700
47701     updateHeaders : function(){
47702         var html = this.renderHeaders();
47703         this.lockedHd.update(html[0]);
47704         this.mainHd.update(html[1]);
47705     },
47706
47707     /**
47708      * Focuses the specified row.
47709      * @param {Number} row The row index
47710      */
47711     focusRow : function(row)
47712     {
47713         //Roo.log('GridView.focusRow');
47714         var x = this.scroller.dom.scrollLeft;
47715         this.focusCell(row, 0, false);
47716         this.scroller.dom.scrollLeft = x;
47717     },
47718
47719     /**
47720      * Focuses the specified cell.
47721      * @param {Number} row The row index
47722      * @param {Number} col The column index
47723      * @param {Boolean} hscroll false to disable horizontal scrolling
47724      */
47725     focusCell : function(row, col, hscroll)
47726     {
47727         //Roo.log('GridView.focusCell');
47728         var el = this.ensureVisible(row, col, hscroll);
47729         this.focusEl.alignTo(el, "tl-tl");
47730         if(Roo.isGecko){
47731             this.focusEl.focus();
47732         }else{
47733             this.focusEl.focus.defer(1, this.focusEl);
47734         }
47735     },
47736
47737     /**
47738      * Scrolls the specified cell into view
47739      * @param {Number} row The row index
47740      * @param {Number} col The column index
47741      * @param {Boolean} hscroll false to disable horizontal scrolling
47742      */
47743     ensureVisible : function(row, col, hscroll)
47744     {
47745         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
47746         //return null; //disable for testing.
47747         if(typeof row != "number"){
47748             row = row.rowIndex;
47749         }
47750         if(row < 0 && row >= this.ds.getCount()){
47751             return  null;
47752         }
47753         col = (col !== undefined ? col : 0);
47754         var cm = this.grid.colModel;
47755         while(cm.isHidden(col)){
47756             col++;
47757         }
47758
47759         var el = this.getCell(row, col);
47760         if(!el){
47761             return null;
47762         }
47763         var c = this.scroller.dom;
47764
47765         var ctop = parseInt(el.offsetTop, 10);
47766         var cleft = parseInt(el.offsetLeft, 10);
47767         var cbot = ctop + el.offsetHeight;
47768         var cright = cleft + el.offsetWidth;
47769         
47770         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
47771         var stop = parseInt(c.scrollTop, 10);
47772         var sleft = parseInt(c.scrollLeft, 10);
47773         var sbot = stop + ch;
47774         var sright = sleft + c.clientWidth;
47775         /*
47776         Roo.log('GridView.ensureVisible:' +
47777                 ' ctop:' + ctop +
47778                 ' c.clientHeight:' + c.clientHeight +
47779                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
47780                 ' stop:' + stop +
47781                 ' cbot:' + cbot +
47782                 ' sbot:' + sbot +
47783                 ' ch:' + ch  
47784                 );
47785         */
47786         if(ctop < stop){
47787              c.scrollTop = ctop;
47788             //Roo.log("set scrolltop to ctop DISABLE?");
47789         }else if(cbot > sbot){
47790             //Roo.log("set scrolltop to cbot-ch");
47791             c.scrollTop = cbot-ch;
47792         }
47793         
47794         if(hscroll !== false){
47795             if(cleft < sleft){
47796                 c.scrollLeft = cleft;
47797             }else if(cright > sright){
47798                 c.scrollLeft = cright-c.clientWidth;
47799             }
47800         }
47801          
47802         return el;
47803     },
47804
47805     updateColumns : function(){
47806         this.grid.stopEditing();
47807         var cm = this.grid.colModel, colIds = this.getColumnIds();
47808         //var totalWidth = cm.getTotalWidth();
47809         var pos = 0;
47810         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47811             //if(cm.isHidden(i)) continue;
47812             var w = cm.getColumnWidth(i);
47813             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47814             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47815         }
47816         this.updateSplitters();
47817     },
47818
47819     generateRules : function(cm){
47820         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
47821         Roo.util.CSS.removeStyleSheet(rulesId);
47822         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47823             var cid = cm.getColumnId(i);
47824             var align = '';
47825             if(cm.config[i].align){
47826                 align = 'text-align:'+cm.config[i].align+';';
47827             }
47828             var hidden = '';
47829             if(cm.isHidden(i)){
47830                 hidden = 'display:none;';
47831             }
47832             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
47833             ruleBuf.push(
47834                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
47835                     this.hdSelector, cid, " {\n", align, width, "}\n",
47836                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
47837                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
47838         }
47839         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47840     },
47841
47842     updateSplitters : function(){
47843         var cm = this.cm, s = this.getSplitters();
47844         if(s){ // splitters not created yet
47845             var pos = 0, locked = true;
47846             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47847                 if(cm.isHidden(i)) continue;
47848                 var w = cm.getColumnWidth(i); // make sure it's a number
47849                 if(!cm.isLocked(i) && locked){
47850                     pos = 0;
47851                     locked = false;
47852                 }
47853                 pos += w;
47854                 s[i].style.left = (pos-this.splitOffset) + "px";
47855             }
47856         }
47857     },
47858
47859     handleHiddenChange : function(colModel, colIndex, hidden){
47860         if(hidden){
47861             this.hideColumn(colIndex);
47862         }else{
47863             this.unhideColumn(colIndex);
47864         }
47865     },
47866
47867     hideColumn : function(colIndex){
47868         var cid = this.getColumnId(colIndex);
47869         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
47870         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
47871         if(Roo.isSafari){
47872             this.updateHeaders();
47873         }
47874         this.updateSplitters();
47875         this.layout();
47876     },
47877
47878     unhideColumn : function(colIndex){
47879         var cid = this.getColumnId(colIndex);
47880         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
47881         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
47882
47883         if(Roo.isSafari){
47884             this.updateHeaders();
47885         }
47886         this.updateSplitters();
47887         this.layout();
47888     },
47889
47890     insertRows : function(dm, firstRow, lastRow, isUpdate){
47891         if(firstRow == 0 && lastRow == dm.getCount()-1){
47892             this.refresh();
47893         }else{
47894             if(!isUpdate){
47895                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
47896             }
47897             var s = this.getScrollState();
47898             var markup = this.renderRows(firstRow, lastRow);
47899             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
47900             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
47901             this.restoreScroll(s);
47902             if(!isUpdate){
47903                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
47904                 this.syncRowHeights(firstRow, lastRow);
47905                 this.stripeRows(firstRow);
47906                 this.layout();
47907             }
47908         }
47909     },
47910
47911     bufferRows : function(markup, target, index){
47912         var before = null, trows = target.rows, tbody = target.tBodies[0];
47913         if(index < trows.length){
47914             before = trows[index];
47915         }
47916         var b = document.createElement("div");
47917         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
47918         var rows = b.firstChild.rows;
47919         for(var i = 0, len = rows.length; i < len; i++){
47920             if(before){
47921                 tbody.insertBefore(rows[0], before);
47922             }else{
47923                 tbody.appendChild(rows[0]);
47924             }
47925         }
47926         b.innerHTML = "";
47927         b = null;
47928     },
47929
47930     deleteRows : function(dm, firstRow, lastRow){
47931         if(dm.getRowCount()<1){
47932             this.fireEvent("beforerefresh", this);
47933             this.mainBody.update("");
47934             this.lockedBody.update("");
47935             this.fireEvent("refresh", this);
47936         }else{
47937             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
47938             var bt = this.getBodyTable();
47939             var tbody = bt.firstChild;
47940             var rows = bt.rows;
47941             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
47942                 tbody.removeChild(rows[firstRow]);
47943             }
47944             this.stripeRows(firstRow);
47945             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
47946         }
47947     },
47948
47949     updateRows : function(dataSource, firstRow, lastRow){
47950         var s = this.getScrollState();
47951         this.refresh();
47952         this.restoreScroll(s);
47953     },
47954
47955     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
47956         if(!noRefresh){
47957            this.refresh();
47958         }
47959         this.updateHeaderSortState();
47960     },
47961
47962     getScrollState : function(){
47963         
47964         var sb = this.scroller.dom;
47965         return {left: sb.scrollLeft, top: sb.scrollTop};
47966     },
47967
47968     stripeRows : function(startRow){
47969         if(!this.grid.stripeRows || this.ds.getCount() < 1){
47970             return;
47971         }
47972         startRow = startRow || 0;
47973         var rows = this.getBodyTable().rows;
47974         var lrows = this.getLockedTable().rows;
47975         var cls = ' x-grid-row-alt ';
47976         for(var i = startRow, len = rows.length; i < len; i++){
47977             var row = rows[i], lrow = lrows[i];
47978             var isAlt = ((i+1) % 2 == 0);
47979             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
47980             if(isAlt == hasAlt){
47981                 continue;
47982             }
47983             if(isAlt){
47984                 row.className += " x-grid-row-alt";
47985             }else{
47986                 row.className = row.className.replace("x-grid-row-alt", "");
47987             }
47988             if(lrow){
47989                 lrow.className = row.className;
47990             }
47991         }
47992     },
47993
47994     restoreScroll : function(state){
47995         //Roo.log('GridView.restoreScroll');
47996         var sb = this.scroller.dom;
47997         sb.scrollLeft = state.left;
47998         sb.scrollTop = state.top;
47999         this.syncScroll();
48000     },
48001
48002     syncScroll : function(){
48003         //Roo.log('GridView.syncScroll');
48004         var sb = this.scroller.dom;
48005         var sh = this.mainHd.dom;
48006         var bs = this.mainBody.dom;
48007         var lv = this.lockedBody.dom;
48008         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
48009         lv.scrollTop = bs.scrollTop = sb.scrollTop;
48010     },
48011
48012     handleScroll : function(e){
48013         this.syncScroll();
48014         var sb = this.scroller.dom;
48015         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
48016         e.stopEvent();
48017     },
48018
48019     handleWheel : function(e){
48020         var d = e.getWheelDelta();
48021         this.scroller.dom.scrollTop -= d*22;
48022         // set this here to prevent jumpy scrolling on large tables
48023         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
48024         e.stopEvent();
48025     },
48026
48027     renderRows : function(startRow, endRow){
48028         // pull in all the crap needed to render rows
48029         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
48030         var colCount = cm.getColumnCount();
48031
48032         if(ds.getCount() < 1){
48033             return ["", ""];
48034         }
48035
48036         // build a map for all the columns
48037         var cs = [];
48038         for(var i = 0; i < colCount; i++){
48039             var name = cm.getDataIndex(i);
48040             cs[i] = {
48041                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
48042                 renderer : cm.getRenderer(i),
48043                 id : cm.getColumnId(i),
48044                 locked : cm.isLocked(i)
48045             };
48046         }
48047
48048         startRow = startRow || 0;
48049         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
48050
48051         // records to render
48052         var rs = ds.getRange(startRow, endRow);
48053
48054         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
48055     },
48056
48057     // As much as I hate to duplicate code, this was branched because FireFox really hates
48058     // [].join("") on strings. The performance difference was substantial enough to
48059     // branch this function
48060     doRender : Roo.isGecko ?
48061             function(cs, rs, ds, startRow, colCount, stripe){
48062                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48063                 // buffers
48064                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48065                 
48066                 var hasListener = this.grid.hasListener('rowclass');
48067                 var rowcfg = {};
48068                 for(var j = 0, len = rs.length; j < len; j++){
48069                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
48070                     for(var i = 0; i < colCount; i++){
48071                         c = cs[i];
48072                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48073                         p.id = c.id;
48074                         p.css = p.attr = "";
48075                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48076                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48077                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48078                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48079                         }
48080                         var markup = ct.apply(p);
48081                         if(!c.locked){
48082                             cb+= markup;
48083                         }else{
48084                             lcb+= markup;
48085                         }
48086                     }
48087                     var alt = [];
48088                     if(stripe && ((rowIndex+1) % 2 == 0)){
48089                         alt.push("x-grid-row-alt")
48090                     }
48091                     if(r.dirty){
48092                         alt.push(  " x-grid-dirty-row");
48093                     }
48094                     rp.cells = lcb;
48095                     if(this.getRowClass){
48096                         alt.push(this.getRowClass(r, rowIndex));
48097                     }
48098                     if (hasListener) {
48099                         rowcfg = {
48100                              
48101                             record: r,
48102                             rowIndex : rowIndex,
48103                             rowClass : ''
48104                         }
48105                         this.grid.fireEvent('rowclass', this, rowcfg);
48106                         alt.push(rowcfg.rowClass);
48107                     }
48108                     rp.alt = alt.join(" ");
48109                     lbuf+= rt.apply(rp);
48110                     rp.cells = cb;
48111                     buf+=  rt.apply(rp);
48112                 }
48113                 return [lbuf, buf];
48114             } :
48115             function(cs, rs, ds, startRow, colCount, stripe){
48116                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48117                 // buffers
48118                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48119                 var hasListener = this.grid.hasListener('rowclass');
48120                 var rowcfg = {};
48121                 for(var j = 0, len = rs.length; j < len; j++){
48122                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
48123                     for(var i = 0; i < colCount; i++){
48124                         c = cs[i];
48125                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48126                         p.id = c.id;
48127                         p.css = p.attr = "";
48128                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48129                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48130                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48131                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48132                         }
48133                         var markup = ct.apply(p);
48134                         if(!c.locked){
48135                             cb[cb.length] = markup;
48136                         }else{
48137                             lcb[lcb.length] = markup;
48138                         }
48139                     }
48140                     var alt = [];
48141                     if(stripe && ((rowIndex+1) % 2 == 0)){
48142                         alt.push( "x-grid-row-alt");
48143                     }
48144                     if(r.dirty){
48145                         alt.push(" x-grid-dirty-row");
48146                     }
48147                     rp.cells = lcb;
48148                     if(this.getRowClass){
48149                         alt.push( this.getRowClass(r, rowIndex));
48150                     }
48151                     if (hasListener) {
48152                         rowcfg = {
48153                              
48154                             record: r,
48155                             rowIndex : rowIndex,
48156                             rowClass : ''
48157                         }
48158                         this.grid.fireEvent('rowclass', this, rowcfg);
48159                         alt.push(rowcfg.rowClass);
48160                     }
48161                     rp.alt = alt.join(" ");
48162                     rp.cells = lcb.join("");
48163                     lbuf[lbuf.length] = rt.apply(rp);
48164                     rp.cells = cb.join("");
48165                     buf[buf.length] =  rt.apply(rp);
48166                 }
48167                 return [lbuf.join(""), buf.join("")];
48168             },
48169
48170     renderBody : function(){
48171         var markup = this.renderRows();
48172         var bt = this.templates.body;
48173         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
48174     },
48175
48176     /**
48177      * Refreshes the grid
48178      * @param {Boolean} headersToo
48179      */
48180     refresh : function(headersToo){
48181         this.fireEvent("beforerefresh", this);
48182         this.grid.stopEditing();
48183         var result = this.renderBody();
48184         this.lockedBody.update(result[0]);
48185         this.mainBody.update(result[1]);
48186         if(headersToo === true){
48187             this.updateHeaders();
48188             this.updateColumns();
48189             this.updateSplitters();
48190             this.updateHeaderSortState();
48191         }
48192         this.syncRowHeights();
48193         this.layout();
48194         this.fireEvent("refresh", this);
48195     },
48196
48197     handleColumnMove : function(cm, oldIndex, newIndex){
48198         this.indexMap = null;
48199         var s = this.getScrollState();
48200         this.refresh(true);
48201         this.restoreScroll(s);
48202         this.afterMove(newIndex);
48203     },
48204
48205     afterMove : function(colIndex){
48206         if(this.enableMoveAnim && Roo.enableFx){
48207             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
48208         }
48209     },
48210
48211     updateCell : function(dm, rowIndex, dataIndex){
48212         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
48213         if(typeof colIndex == "undefined"){ // not present in grid
48214             return;
48215         }
48216         var cm = this.grid.colModel;
48217         var cell = this.getCell(rowIndex, colIndex);
48218         var cellText = this.getCellText(rowIndex, colIndex);
48219
48220         var p = {
48221             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
48222             id : cm.getColumnId(colIndex),
48223             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
48224         };
48225         var renderer = cm.getRenderer(colIndex);
48226         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
48227         if(typeof val == "undefined" || val === "") val = "&#160;";
48228         cellText.innerHTML = val;
48229         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
48230         this.syncRowHeights(rowIndex, rowIndex);
48231     },
48232
48233     calcColumnWidth : function(colIndex, maxRowsToMeasure){
48234         var maxWidth = 0;
48235         if(this.grid.autoSizeHeaders){
48236             var h = this.getHeaderCellMeasure(colIndex);
48237             maxWidth = Math.max(maxWidth, h.scrollWidth);
48238         }
48239         var tb, index;
48240         if(this.cm.isLocked(colIndex)){
48241             tb = this.getLockedTable();
48242             index = colIndex;
48243         }else{
48244             tb = this.getBodyTable();
48245             index = colIndex - this.cm.getLockedCount();
48246         }
48247         if(tb && tb.rows){
48248             var rows = tb.rows;
48249             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
48250             for(var i = 0; i < stopIndex; i++){
48251                 var cell = rows[i].childNodes[index].firstChild;
48252                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
48253             }
48254         }
48255         return maxWidth + /*margin for error in IE*/ 5;
48256     },
48257     /**
48258      * Autofit a column to its content.
48259      * @param {Number} colIndex
48260      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
48261      */
48262      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
48263          if(this.cm.isHidden(colIndex)){
48264              return; // can't calc a hidden column
48265          }
48266         if(forceMinSize){
48267             var cid = this.cm.getColumnId(colIndex);
48268             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
48269            if(this.grid.autoSizeHeaders){
48270                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
48271            }
48272         }
48273         var newWidth = this.calcColumnWidth(colIndex);
48274         this.cm.setColumnWidth(colIndex,
48275             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
48276         if(!suppressEvent){
48277             this.grid.fireEvent("columnresize", colIndex, newWidth);
48278         }
48279     },
48280
48281     /**
48282      * Autofits all columns to their content and then expands to fit any extra space in the grid
48283      */
48284      autoSizeColumns : function(){
48285         var cm = this.grid.colModel;
48286         var colCount = cm.getColumnCount();
48287         for(var i = 0; i < colCount; i++){
48288             this.autoSizeColumn(i, true, true);
48289         }
48290         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
48291             this.fitColumns();
48292         }else{
48293             this.updateColumns();
48294             this.layout();
48295         }
48296     },
48297
48298     /**
48299      * Autofits all columns to the grid's width proportionate with their current size
48300      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
48301      */
48302     fitColumns : function(reserveScrollSpace){
48303         var cm = this.grid.colModel;
48304         var colCount = cm.getColumnCount();
48305         var cols = [];
48306         var width = 0;
48307         var i, w;
48308         for (i = 0; i < colCount; i++){
48309             if(!cm.isHidden(i) && !cm.isFixed(i)){
48310                 w = cm.getColumnWidth(i);
48311                 cols.push(i);
48312                 cols.push(w);
48313                 width += w;
48314             }
48315         }
48316         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
48317         if(reserveScrollSpace){
48318             avail -= 17;
48319         }
48320         var frac = (avail - cm.getTotalWidth())/width;
48321         while (cols.length){
48322             w = cols.pop();
48323             i = cols.pop();
48324             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
48325         }
48326         this.updateColumns();
48327         this.layout();
48328     },
48329
48330     onRowSelect : function(rowIndex){
48331         var row = this.getRowComposite(rowIndex);
48332         row.addClass("x-grid-row-selected");
48333     },
48334
48335     onRowDeselect : function(rowIndex){
48336         var row = this.getRowComposite(rowIndex);
48337         row.removeClass("x-grid-row-selected");
48338     },
48339
48340     onCellSelect : function(row, col){
48341         var cell = this.getCell(row, col);
48342         if(cell){
48343             Roo.fly(cell).addClass("x-grid-cell-selected");
48344         }
48345     },
48346
48347     onCellDeselect : function(row, col){
48348         var cell = this.getCell(row, col);
48349         if(cell){
48350             Roo.fly(cell).removeClass("x-grid-cell-selected");
48351         }
48352     },
48353
48354     updateHeaderSortState : function(){
48355         var state = this.ds.getSortState();
48356         if(!state){
48357             return;
48358         }
48359         this.sortState = state;
48360         var sortColumn = this.cm.findColumnIndex(state.field);
48361         if(sortColumn != -1){
48362             var sortDir = state.direction;
48363             var sc = this.sortClasses;
48364             var hds = this.el.select(this.headerSelector).removeClass(sc);
48365             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
48366         }
48367     },
48368
48369     handleHeaderClick : function(g, index){
48370         if(this.headersDisabled){
48371             return;
48372         }
48373         var dm = g.dataSource, cm = g.colModel;
48374         if(!cm.isSortable(index)){
48375             return;
48376         }
48377         g.stopEditing();
48378         dm.sort(cm.getDataIndex(index));
48379     },
48380
48381
48382     destroy : function(){
48383         if(this.colMenu){
48384             this.colMenu.removeAll();
48385             Roo.menu.MenuMgr.unregister(this.colMenu);
48386             this.colMenu.getEl().remove();
48387             delete this.colMenu;
48388         }
48389         if(this.hmenu){
48390             this.hmenu.removeAll();
48391             Roo.menu.MenuMgr.unregister(this.hmenu);
48392             this.hmenu.getEl().remove();
48393             delete this.hmenu;
48394         }
48395         if(this.grid.enableColumnMove){
48396             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48397             if(dds){
48398                 for(var dd in dds){
48399                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
48400                         var elid = dds[dd].dragElId;
48401                         dds[dd].unreg();
48402                         Roo.get(elid).remove();
48403                     } else if(dds[dd].config.isTarget){
48404                         dds[dd].proxyTop.remove();
48405                         dds[dd].proxyBottom.remove();
48406                         dds[dd].unreg();
48407                     }
48408                     if(Roo.dd.DDM.locationCache[dd]){
48409                         delete Roo.dd.DDM.locationCache[dd];
48410                     }
48411                 }
48412                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48413             }
48414         }
48415         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48416         this.bind(null, null);
48417         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48418     },
48419
48420     handleLockChange : function(){
48421         this.refresh(true);
48422     },
48423
48424     onDenyColumnLock : function(){
48425
48426     },
48427
48428     onDenyColumnHide : function(){
48429
48430     },
48431
48432     handleHdMenuClick : function(item){
48433         var index = this.hdCtxIndex;
48434         var cm = this.cm, ds = this.ds;
48435         switch(item.id){
48436             case "asc":
48437                 ds.sort(cm.getDataIndex(index), "ASC");
48438                 break;
48439             case "desc":
48440                 ds.sort(cm.getDataIndex(index), "DESC");
48441                 break;
48442             case "lock":
48443                 var lc = cm.getLockedCount();
48444                 if(cm.getColumnCount(true) <= lc+1){
48445                     this.onDenyColumnLock();
48446                     return;
48447                 }
48448                 if(lc != index){
48449                     cm.setLocked(index, true, true);
48450                     cm.moveColumn(index, lc);
48451                     this.grid.fireEvent("columnmove", index, lc);
48452                 }else{
48453                     cm.setLocked(index, true);
48454                 }
48455             break;
48456             case "unlock":
48457                 var lc = cm.getLockedCount();
48458                 if((lc-1) != index){
48459                     cm.setLocked(index, false, true);
48460                     cm.moveColumn(index, lc-1);
48461                     this.grid.fireEvent("columnmove", index, lc-1);
48462                 }else{
48463                     cm.setLocked(index, false);
48464                 }
48465             break;
48466             default:
48467                 index = cm.getIndexById(item.id.substr(4));
48468                 if(index != -1){
48469                     if(item.checked && cm.getColumnCount(true) <= 1){
48470                         this.onDenyColumnHide();
48471                         return false;
48472                     }
48473                     cm.setHidden(index, item.checked);
48474                 }
48475         }
48476         return true;
48477     },
48478
48479     beforeColMenuShow : function(){
48480         var cm = this.cm,  colCount = cm.getColumnCount();
48481         this.colMenu.removeAll();
48482         for(var i = 0; i < colCount; i++){
48483             this.colMenu.add(new Roo.menu.CheckItem({
48484                 id: "col-"+cm.getColumnId(i),
48485                 text: cm.getColumnHeader(i),
48486                 checked: !cm.isHidden(i),
48487                 hideOnClick:false
48488             }));
48489         }
48490     },
48491
48492     handleHdCtx : function(g, index, e){
48493         e.stopEvent();
48494         var hd = this.getHeaderCell(index);
48495         this.hdCtxIndex = index;
48496         var ms = this.hmenu.items, cm = this.cm;
48497         ms.get("asc").setDisabled(!cm.isSortable(index));
48498         ms.get("desc").setDisabled(!cm.isSortable(index));
48499         if(this.grid.enableColLock !== false){
48500             ms.get("lock").setDisabled(cm.isLocked(index));
48501             ms.get("unlock").setDisabled(!cm.isLocked(index));
48502         }
48503         this.hmenu.show(hd, "tl-bl");
48504     },
48505
48506     handleHdOver : function(e){
48507         var hd = this.findHeaderCell(e.getTarget());
48508         if(hd && !this.headersDisabled){
48509             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48510                this.fly(hd).addClass("x-grid-hd-over");
48511             }
48512         }
48513     },
48514
48515     handleHdOut : function(e){
48516         var hd = this.findHeaderCell(e.getTarget());
48517         if(hd){
48518             this.fly(hd).removeClass("x-grid-hd-over");
48519         }
48520     },
48521
48522     handleSplitDblClick : function(e, t){
48523         var i = this.getCellIndex(t);
48524         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48525             this.autoSizeColumn(i, true);
48526             this.layout();
48527         }
48528     },
48529
48530     render : function(){
48531
48532         var cm = this.cm;
48533         var colCount = cm.getColumnCount();
48534
48535         if(this.grid.monitorWindowResize === true){
48536             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48537         }
48538         var header = this.renderHeaders();
48539         var body = this.templates.body.apply({rows:""});
48540         var html = this.templates.master.apply({
48541             lockedBody: body,
48542             body: body,
48543             lockedHeader: header[0],
48544             header: header[1]
48545         });
48546
48547         //this.updateColumns();
48548
48549         this.grid.getGridEl().dom.innerHTML = html;
48550
48551         this.initElements();
48552         
48553         // a kludge to fix the random scolling effect in webkit
48554         this.el.on("scroll", function() {
48555             this.el.dom.scrollTop=0; // hopefully not recursive..
48556         },this);
48557
48558         this.scroller.on("scroll", this.handleScroll, this);
48559         this.lockedBody.on("mousewheel", this.handleWheel, this);
48560         this.mainBody.on("mousewheel", this.handleWheel, this);
48561
48562         this.mainHd.on("mouseover", this.handleHdOver, this);
48563         this.mainHd.on("mouseout", this.handleHdOut, this);
48564         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48565                 {delegate: "."+this.splitClass});
48566
48567         this.lockedHd.on("mouseover", this.handleHdOver, this);
48568         this.lockedHd.on("mouseout", this.handleHdOut, this);
48569         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48570                 {delegate: "."+this.splitClass});
48571
48572         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48573             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48574         }
48575
48576         this.updateSplitters();
48577
48578         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48579             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48580             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48581         }
48582
48583         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48584             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48585             this.hmenu.add(
48586                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48587                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48588             );
48589             if(this.grid.enableColLock !== false){
48590                 this.hmenu.add('-',
48591                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48592                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48593                 );
48594             }
48595             if(this.grid.enableColumnHide !== false){
48596
48597                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48598                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48599                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48600
48601                 this.hmenu.add('-',
48602                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48603                 );
48604             }
48605             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48606
48607             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48608         }
48609
48610         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48611             this.dd = new Roo.grid.GridDragZone(this.grid, {
48612                 ddGroup : this.grid.ddGroup || 'GridDD'
48613             });
48614         }
48615
48616         /*
48617         for(var i = 0; i < colCount; i++){
48618             if(cm.isHidden(i)){
48619                 this.hideColumn(i);
48620             }
48621             if(cm.config[i].align){
48622                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48623                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48624             }
48625         }*/
48626         
48627         this.updateHeaderSortState();
48628
48629         this.beforeInitialResize();
48630         this.layout(true);
48631
48632         // two part rendering gives faster view to the user
48633         this.renderPhase2.defer(1, this);
48634     },
48635
48636     renderPhase2 : function(){
48637         // render the rows now
48638         this.refresh();
48639         if(this.grid.autoSizeColumns){
48640             this.autoSizeColumns();
48641         }
48642     },
48643
48644     beforeInitialResize : function(){
48645
48646     },
48647
48648     onColumnSplitterMoved : function(i, w){
48649         this.userResized = true;
48650         var cm = this.grid.colModel;
48651         cm.setColumnWidth(i, w, true);
48652         var cid = cm.getColumnId(i);
48653         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48654         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48655         this.updateSplitters();
48656         this.layout();
48657         this.grid.fireEvent("columnresize", i, w);
48658     },
48659
48660     syncRowHeights : function(startIndex, endIndex){
48661         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48662             startIndex = startIndex || 0;
48663             var mrows = this.getBodyTable().rows;
48664             var lrows = this.getLockedTable().rows;
48665             var len = mrows.length-1;
48666             endIndex = Math.min(endIndex || len, len);
48667             for(var i = startIndex; i <= endIndex; i++){
48668                 var m = mrows[i], l = lrows[i];
48669                 var h = Math.max(m.offsetHeight, l.offsetHeight);
48670                 m.style.height = l.style.height = h + "px";
48671             }
48672         }
48673     },
48674
48675     layout : function(initialRender, is2ndPass){
48676         var g = this.grid;
48677         var auto = g.autoHeight;
48678         var scrollOffset = 16;
48679         var c = g.getGridEl(), cm = this.cm,
48680                 expandCol = g.autoExpandColumn,
48681                 gv = this;
48682         //c.beginMeasure();
48683
48684         if(!c.dom.offsetWidth){ // display:none?
48685             if(initialRender){
48686                 this.lockedWrap.show();
48687                 this.mainWrap.show();
48688             }
48689             return;
48690         }
48691
48692         var hasLock = this.cm.isLocked(0);
48693
48694         var tbh = this.headerPanel.getHeight();
48695         var bbh = this.footerPanel.getHeight();
48696
48697         if(auto){
48698             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
48699             var newHeight = ch + c.getBorderWidth("tb");
48700             if(g.maxHeight){
48701                 newHeight = Math.min(g.maxHeight, newHeight);
48702             }
48703             c.setHeight(newHeight);
48704         }
48705
48706         if(g.autoWidth){
48707             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
48708         }
48709
48710         var s = this.scroller;
48711
48712         var csize = c.getSize(true);
48713
48714         this.el.setSize(csize.width, csize.height);
48715
48716         this.headerPanel.setWidth(csize.width);
48717         this.footerPanel.setWidth(csize.width);
48718
48719         var hdHeight = this.mainHd.getHeight();
48720         var vw = csize.width;
48721         var vh = csize.height - (tbh + bbh);
48722
48723         s.setSize(vw, vh);
48724
48725         var bt = this.getBodyTable();
48726         var ltWidth = hasLock ?
48727                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
48728
48729         var scrollHeight = bt.offsetHeight;
48730         var scrollWidth = ltWidth + bt.offsetWidth;
48731         var vscroll = false, hscroll = false;
48732
48733         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
48734
48735         var lw = this.lockedWrap, mw = this.mainWrap;
48736         var lb = this.lockedBody, mb = this.mainBody;
48737
48738         setTimeout(function(){
48739             var t = s.dom.offsetTop;
48740             var w = s.dom.clientWidth,
48741                 h = s.dom.clientHeight;
48742
48743             lw.setTop(t);
48744             lw.setSize(ltWidth, h);
48745
48746             mw.setLeftTop(ltWidth, t);
48747             mw.setSize(w-ltWidth, h);
48748
48749             lb.setHeight(h-hdHeight);
48750             mb.setHeight(h-hdHeight);
48751
48752             if(is2ndPass !== true && !gv.userResized && expandCol){
48753                 // high speed resize without full column calculation
48754                 
48755                 var ci = cm.getIndexById(expandCol);
48756                 if (ci < 0) {
48757                     ci = cm.findColumnIndex(expandCol);
48758                 }
48759                 ci = Math.max(0, ci); // make sure it's got at least the first col.
48760                 var expandId = cm.getColumnId(ci);
48761                 var  tw = cm.getTotalWidth(false);
48762                 var currentWidth = cm.getColumnWidth(ci);
48763                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
48764                 if(currentWidth != cw){
48765                     cm.setColumnWidth(ci, cw, true);
48766                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48767                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48768                     gv.updateSplitters();
48769                     gv.layout(false, true);
48770                 }
48771             }
48772
48773             if(initialRender){
48774                 lw.show();
48775                 mw.show();
48776             }
48777             //c.endMeasure();
48778         }, 10);
48779     },
48780
48781     onWindowResize : function(){
48782         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
48783             return;
48784         }
48785         this.layout();
48786     },
48787
48788     appendFooter : function(parentEl){
48789         return null;
48790     },
48791
48792     sortAscText : "Sort Ascending",
48793     sortDescText : "Sort Descending",
48794     lockText : "Lock Column",
48795     unlockText : "Unlock Column",
48796     columnsText : "Columns"
48797 });
48798
48799
48800 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
48801     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
48802     this.proxy.el.addClass('x-grid3-col-dd');
48803 };
48804
48805 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
48806     handleMouseDown : function(e){
48807
48808     },
48809
48810     callHandleMouseDown : function(e){
48811         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
48812     }
48813 });
48814 /*
48815  * Based on:
48816  * Ext JS Library 1.1.1
48817  * Copyright(c) 2006-2007, Ext JS, LLC.
48818  *
48819  * Originally Released Under LGPL - original licence link has changed is not relivant.
48820  *
48821  * Fork - LGPL
48822  * <script type="text/javascript">
48823  */
48824  
48825 // private
48826 // This is a support class used internally by the Grid components
48827 Roo.grid.SplitDragZone = function(grid, hd, hd2){
48828     this.grid = grid;
48829     this.view = grid.getView();
48830     this.proxy = this.view.resizeProxy;
48831     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
48832         "gridSplitters" + this.grid.getGridEl().id, {
48833         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
48834     });
48835     this.setHandleElId(Roo.id(hd));
48836     this.setOuterHandleElId(Roo.id(hd2));
48837     this.scroll = false;
48838 };
48839 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
48840     fly: Roo.Element.fly,
48841
48842     b4StartDrag : function(x, y){
48843         this.view.headersDisabled = true;
48844         this.proxy.setHeight(this.view.mainWrap.getHeight());
48845         var w = this.cm.getColumnWidth(this.cellIndex);
48846         var minw = Math.max(w-this.grid.minColumnWidth, 0);
48847         this.resetConstraints();
48848         this.setXConstraint(minw, 1000);
48849         this.setYConstraint(0, 0);
48850         this.minX = x - minw;
48851         this.maxX = x + 1000;
48852         this.startPos = x;
48853         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
48854     },
48855
48856
48857     handleMouseDown : function(e){
48858         ev = Roo.EventObject.setEvent(e);
48859         var t = this.fly(ev.getTarget());
48860         if(t.hasClass("x-grid-split")){
48861             this.cellIndex = this.view.getCellIndex(t.dom);
48862             this.split = t.dom;
48863             this.cm = this.grid.colModel;
48864             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
48865                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
48866             }
48867         }
48868     },
48869
48870     endDrag : function(e){
48871         this.view.headersDisabled = false;
48872         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
48873         var diff = endX - this.startPos;
48874         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
48875     },
48876
48877     autoOffset : function(){
48878         this.setDelta(0,0);
48879     }
48880 });/*
48881  * Based on:
48882  * Ext JS Library 1.1.1
48883  * Copyright(c) 2006-2007, Ext JS, LLC.
48884  *
48885  * Originally Released Under LGPL - original licence link has changed is not relivant.
48886  *
48887  * Fork - LGPL
48888  * <script type="text/javascript">
48889  */
48890  
48891 // private
48892 // This is a support class used internally by the Grid components
48893 Roo.grid.GridDragZone = function(grid, config){
48894     this.view = grid.getView();
48895     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
48896     if(this.view.lockedBody){
48897         this.setHandleElId(Roo.id(this.view.mainBody.dom));
48898         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
48899     }
48900     this.scroll = false;
48901     this.grid = grid;
48902     this.ddel = document.createElement('div');
48903     this.ddel.className = 'x-grid-dd-wrap';
48904 };
48905
48906 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
48907     ddGroup : "GridDD",
48908
48909     getDragData : function(e){
48910         var t = Roo.lib.Event.getTarget(e);
48911         var rowIndex = this.view.findRowIndex(t);
48912         if(rowIndex !== false){
48913             var sm = this.grid.selModel;
48914             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
48915               //  sm.mouseDown(e, t);
48916             //}
48917             if (e.hasModifier()){
48918                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
48919             }
48920             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
48921         }
48922         return false;
48923     },
48924
48925     onInitDrag : function(e){
48926         var data = this.dragData;
48927         this.ddel.innerHTML = this.grid.getDragDropText();
48928         this.proxy.update(this.ddel);
48929         // fire start drag?
48930     },
48931
48932     afterRepair : function(){
48933         this.dragging = false;
48934     },
48935
48936     getRepairXY : function(e, data){
48937         return false;
48938     },
48939
48940     onEndDrag : function(data, e){
48941         // fire end drag?
48942     },
48943
48944     onValidDrop : function(dd, e, id){
48945         // fire drag drop?
48946         this.hideProxy();
48947     },
48948
48949     beforeInvalidDrop : function(e, id){
48950
48951     }
48952 });/*
48953  * Based on:
48954  * Ext JS Library 1.1.1
48955  * Copyright(c) 2006-2007, Ext JS, LLC.
48956  *
48957  * Originally Released Under LGPL - original licence link has changed is not relivant.
48958  *
48959  * Fork - LGPL
48960  * <script type="text/javascript">
48961  */
48962  
48963
48964 /**
48965  * @class Roo.grid.ColumnModel
48966  * @extends Roo.util.Observable
48967  * This is the default implementation of a ColumnModel used by the Grid. It defines
48968  * the columns in the grid.
48969  * <br>Usage:<br>
48970  <pre><code>
48971  var colModel = new Roo.grid.ColumnModel([
48972         {header: "Ticker", width: 60, sortable: true, locked: true},
48973         {header: "Company Name", width: 150, sortable: true},
48974         {header: "Market Cap.", width: 100, sortable: true},
48975         {header: "$ Sales", width: 100, sortable: true, renderer: money},
48976         {header: "Employees", width: 100, sortable: true, resizable: false}
48977  ]);
48978  </code></pre>
48979  * <p>
48980  
48981  * The config options listed for this class are options which may appear in each
48982  * individual column definition.
48983  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
48984  * @constructor
48985  * @param {Object} config An Array of column config objects. See this class's
48986  * config objects for details.
48987 */
48988 Roo.grid.ColumnModel = function(config){
48989         /**
48990      * The config passed into the constructor
48991      */
48992     this.config = config;
48993     this.lookup = {};
48994
48995     // if no id, create one
48996     // if the column does not have a dataIndex mapping,
48997     // map it to the order it is in the config
48998     for(var i = 0, len = config.length; i < len; i++){
48999         var c = config[i];
49000         if(typeof c.dataIndex == "undefined"){
49001             c.dataIndex = i;
49002         }
49003         if(typeof c.renderer == "string"){
49004             c.renderer = Roo.util.Format[c.renderer];
49005         }
49006         if(typeof c.id == "undefined"){
49007             c.id = Roo.id();
49008         }
49009         if(c.editor && c.editor.xtype){
49010             c.editor  = Roo.factory(c.editor, Roo.grid);
49011         }
49012         if(c.editor && c.editor.isFormField){
49013             c.editor = new Roo.grid.GridEditor(c.editor);
49014         }
49015         this.lookup[c.id] = c;
49016     }
49017
49018     /**
49019      * The width of columns which have no width specified (defaults to 100)
49020      * @type Number
49021      */
49022     this.defaultWidth = 100;
49023
49024     /**
49025      * Default sortable of columns which have no sortable specified (defaults to false)
49026      * @type Boolean
49027      */
49028     this.defaultSortable = false;
49029
49030     this.addEvents({
49031         /**
49032              * @event widthchange
49033              * Fires when the width of a column changes.
49034              * @param {ColumnModel} this
49035              * @param {Number} columnIndex The column index
49036              * @param {Number} newWidth The new width
49037              */
49038             "widthchange": true,
49039         /**
49040              * @event headerchange
49041              * Fires when the text of a header changes.
49042              * @param {ColumnModel} this
49043              * @param {Number} columnIndex The column index
49044              * @param {Number} newText The new header text
49045              */
49046             "headerchange": true,
49047         /**
49048              * @event hiddenchange
49049              * Fires when a column is hidden or "unhidden".
49050              * @param {ColumnModel} this
49051              * @param {Number} columnIndex The column index
49052              * @param {Boolean} hidden true if hidden, false otherwise
49053              */
49054             "hiddenchange": true,
49055             /**
49056          * @event columnmoved
49057          * Fires when a column is moved.
49058          * @param {ColumnModel} this
49059          * @param {Number} oldIndex
49060          * @param {Number} newIndex
49061          */
49062         "columnmoved" : true,
49063         /**
49064          * @event columlockchange
49065          * Fires when a column's locked state is changed
49066          * @param {ColumnModel} this
49067          * @param {Number} colIndex
49068          * @param {Boolean} locked true if locked
49069          */
49070         "columnlockchange" : true
49071     });
49072     Roo.grid.ColumnModel.superclass.constructor.call(this);
49073 };
49074 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
49075     /**
49076      * @cfg {String} header The header text to display in the Grid view.
49077      */
49078     /**
49079      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
49080      * {@link Roo.data.Record} definition from which to draw the column's value. If not
49081      * specified, the column's index is used as an index into the Record's data Array.
49082      */
49083     /**
49084      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
49085      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
49086      */
49087     /**
49088      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
49089      * Defaults to the value of the {@link #defaultSortable} property.
49090      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
49091      */
49092     /**
49093      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
49094      */
49095     /**
49096      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
49097      */
49098     /**
49099      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
49100      */
49101     /**
49102      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
49103      */
49104     /**
49105      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
49106      * given the cell's data value. See {@link #setRenderer}. If not specified, the
49107      * default renderer uses the raw data value.
49108      */
49109        /**
49110      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
49111      */
49112     /**
49113      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
49114      */
49115
49116     /**
49117      * Returns the id of the column at the specified index.
49118      * @param {Number} index The column index
49119      * @return {String} the id
49120      */
49121     getColumnId : function(index){
49122         return this.config[index].id;
49123     },
49124
49125     /**
49126      * Returns the column for a specified id.
49127      * @param {String} id The column id
49128      * @return {Object} the column
49129      */
49130     getColumnById : function(id){
49131         return this.lookup[id];
49132     },
49133
49134     
49135     /**
49136      * Returns the column for a specified dataIndex.
49137      * @param {String} dataIndex The column dataIndex
49138      * @return {Object|Boolean} the column or false if not found
49139      */
49140     getColumnByDataIndex: function(dataIndex){
49141         var index = this.findColumnIndex(dataIndex);
49142         return index > -1 ? this.config[index] : false;
49143     },
49144     
49145     /**
49146      * Returns the index for a specified column id.
49147      * @param {String} id The column id
49148      * @return {Number} the index, or -1 if not found
49149      */
49150     getIndexById : function(id){
49151         for(var i = 0, len = this.config.length; i < len; i++){
49152             if(this.config[i].id == id){
49153                 return i;
49154             }
49155         }
49156         return -1;
49157     },
49158     
49159     /**
49160      * Returns the index for a specified column dataIndex.
49161      * @param {String} dataIndex The column dataIndex
49162      * @return {Number} the index, or -1 if not found
49163      */
49164     
49165     findColumnIndex : function(dataIndex){
49166         for(var i = 0, len = this.config.length; i < len; i++){
49167             if(this.config[i].dataIndex == dataIndex){
49168                 return i;
49169             }
49170         }
49171         return -1;
49172     },
49173     
49174     
49175     moveColumn : function(oldIndex, newIndex){
49176         var c = this.config[oldIndex];
49177         this.config.splice(oldIndex, 1);
49178         this.config.splice(newIndex, 0, c);
49179         this.dataMap = null;
49180         this.fireEvent("columnmoved", this, oldIndex, newIndex);
49181     },
49182
49183     isLocked : function(colIndex){
49184         return this.config[colIndex].locked === true;
49185     },
49186
49187     setLocked : function(colIndex, value, suppressEvent){
49188         if(this.isLocked(colIndex) == value){
49189             return;
49190         }
49191         this.config[colIndex].locked = value;
49192         if(!suppressEvent){
49193             this.fireEvent("columnlockchange", this, colIndex, value);
49194         }
49195     },
49196
49197     getTotalLockedWidth : function(){
49198         var totalWidth = 0;
49199         for(var i = 0; i < this.config.length; i++){
49200             if(this.isLocked(i) && !this.isHidden(i)){
49201                 this.totalWidth += this.getColumnWidth(i);
49202             }
49203         }
49204         return totalWidth;
49205     },
49206
49207     getLockedCount : function(){
49208         for(var i = 0, len = this.config.length; i < len; i++){
49209             if(!this.isLocked(i)){
49210                 return i;
49211             }
49212         }
49213     },
49214
49215     /**
49216      * Returns the number of columns.
49217      * @return {Number}
49218      */
49219     getColumnCount : function(visibleOnly){
49220         if(visibleOnly === true){
49221             var c = 0;
49222             for(var i = 0, len = this.config.length; i < len; i++){
49223                 if(!this.isHidden(i)){
49224                     c++;
49225                 }
49226             }
49227             return c;
49228         }
49229         return this.config.length;
49230     },
49231
49232     /**
49233      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
49234      * @param {Function} fn
49235      * @param {Object} scope (optional)
49236      * @return {Array} result
49237      */
49238     getColumnsBy : function(fn, scope){
49239         var r = [];
49240         for(var i = 0, len = this.config.length; i < len; i++){
49241             var c = this.config[i];
49242             if(fn.call(scope||this, c, i) === true){
49243                 r[r.length] = c;
49244             }
49245         }
49246         return r;
49247     },
49248
49249     /**
49250      * Returns true if the specified column is sortable.
49251      * @param {Number} col The column index
49252      * @return {Boolean}
49253      */
49254     isSortable : function(col){
49255         if(typeof this.config[col].sortable == "undefined"){
49256             return this.defaultSortable;
49257         }
49258         return this.config[col].sortable;
49259     },
49260
49261     /**
49262      * Returns the rendering (formatting) function defined for the column.
49263      * @param {Number} col The column index.
49264      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
49265      */
49266     getRenderer : function(col){
49267         if(!this.config[col].renderer){
49268             return Roo.grid.ColumnModel.defaultRenderer;
49269         }
49270         return this.config[col].renderer;
49271     },
49272
49273     /**
49274      * Sets the rendering (formatting) function for a column.
49275      * @param {Number} col The column index
49276      * @param {Function} fn The function to use to process the cell's raw data
49277      * to return HTML markup for the grid view. The render function is called with
49278      * the following parameters:<ul>
49279      * <li>Data value.</li>
49280      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
49281      * <li>css A CSS style string to apply to the table cell.</li>
49282      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
49283      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
49284      * <li>Row index</li>
49285      * <li>Column index</li>
49286      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
49287      */
49288     setRenderer : function(col, fn){
49289         this.config[col].renderer = fn;
49290     },
49291
49292     /**
49293      * Returns the width for the specified column.
49294      * @param {Number} col The column index
49295      * @return {Number}
49296      */
49297     getColumnWidth : function(col){
49298         return this.config[col].width * 1 || this.defaultWidth;
49299     },
49300
49301     /**
49302      * Sets the width for a column.
49303      * @param {Number} col The column index
49304      * @param {Number} width The new width
49305      */
49306     setColumnWidth : function(col, width, suppressEvent){
49307         this.config[col].width = width;
49308         this.totalWidth = null;
49309         if(!suppressEvent){
49310              this.fireEvent("widthchange", this, col, width);
49311         }
49312     },
49313
49314     /**
49315      * Returns the total width of all columns.
49316      * @param {Boolean} includeHidden True to include hidden column widths
49317      * @return {Number}
49318      */
49319     getTotalWidth : function(includeHidden){
49320         if(!this.totalWidth){
49321             this.totalWidth = 0;
49322             for(var i = 0, len = this.config.length; i < len; i++){
49323                 if(includeHidden || !this.isHidden(i)){
49324                     this.totalWidth += this.getColumnWidth(i);
49325                 }
49326             }
49327         }
49328         return this.totalWidth;
49329     },
49330
49331     /**
49332      * Returns the header for the specified column.
49333      * @param {Number} col The column index
49334      * @return {String}
49335      */
49336     getColumnHeader : function(col){
49337         return this.config[col].header;
49338     },
49339
49340     /**
49341      * Sets the header for a column.
49342      * @param {Number} col The column index
49343      * @param {String} header The new header
49344      */
49345     setColumnHeader : function(col, header){
49346         this.config[col].header = header;
49347         this.fireEvent("headerchange", this, col, header);
49348     },
49349
49350     /**
49351      * Returns the tooltip for the specified column.
49352      * @param {Number} col The column index
49353      * @return {String}
49354      */
49355     getColumnTooltip : function(col){
49356             return this.config[col].tooltip;
49357     },
49358     /**
49359      * Sets the tooltip for a column.
49360      * @param {Number} col The column index
49361      * @param {String} tooltip The new tooltip
49362      */
49363     setColumnTooltip : function(col, tooltip){
49364             this.config[col].tooltip = tooltip;
49365     },
49366
49367     /**
49368      * Returns the dataIndex for the specified column.
49369      * @param {Number} col The column index
49370      * @return {Number}
49371      */
49372     getDataIndex : function(col){
49373         return this.config[col].dataIndex;
49374     },
49375
49376     /**
49377      * Sets the dataIndex for a column.
49378      * @param {Number} col The column index
49379      * @param {Number} dataIndex The new dataIndex
49380      */
49381     setDataIndex : function(col, dataIndex){
49382         this.config[col].dataIndex = dataIndex;
49383     },
49384
49385     
49386     
49387     /**
49388      * Returns true if the cell is editable.
49389      * @param {Number} colIndex The column index
49390      * @param {Number} rowIndex The row index
49391      * @return {Boolean}
49392      */
49393     isCellEditable : function(colIndex, rowIndex){
49394         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
49395     },
49396
49397     /**
49398      * Returns the editor defined for the cell/column.
49399      * return false or null to disable editing.
49400      * @param {Number} colIndex The column index
49401      * @param {Number} rowIndex The row index
49402      * @return {Object}
49403      */
49404     getCellEditor : function(colIndex, rowIndex){
49405         return this.config[colIndex].editor;
49406     },
49407
49408     /**
49409      * Sets if a column is editable.
49410      * @param {Number} col The column index
49411      * @param {Boolean} editable True if the column is editable
49412      */
49413     setEditable : function(col, editable){
49414         this.config[col].editable = editable;
49415     },
49416
49417
49418     /**
49419      * Returns true if the column is hidden.
49420      * @param {Number} colIndex The column index
49421      * @return {Boolean}
49422      */
49423     isHidden : function(colIndex){
49424         return this.config[colIndex].hidden;
49425     },
49426
49427
49428     /**
49429      * Returns true if the column width cannot be changed
49430      */
49431     isFixed : function(colIndex){
49432         return this.config[colIndex].fixed;
49433     },
49434
49435     /**
49436      * Returns true if the column can be resized
49437      * @return {Boolean}
49438      */
49439     isResizable : function(colIndex){
49440         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49441     },
49442     /**
49443      * Sets if a column is hidden.
49444      * @param {Number} colIndex The column index
49445      * @param {Boolean} hidden True if the column is hidden
49446      */
49447     setHidden : function(colIndex, hidden){
49448         this.config[colIndex].hidden = hidden;
49449         this.totalWidth = null;
49450         this.fireEvent("hiddenchange", this, colIndex, hidden);
49451     },
49452
49453     /**
49454      * Sets the editor for a column.
49455      * @param {Number} col The column index
49456      * @param {Object} editor The editor object
49457      */
49458     setEditor : function(col, editor){
49459         this.config[col].editor = editor;
49460     }
49461 });
49462
49463 Roo.grid.ColumnModel.defaultRenderer = function(value){
49464         if(typeof value == "string" && value.length < 1){
49465             return "&#160;";
49466         }
49467         return value;
49468 };
49469
49470 // Alias for backwards compatibility
49471 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49472 /*
49473  * Based on:
49474  * Ext JS Library 1.1.1
49475  * Copyright(c) 2006-2007, Ext JS, LLC.
49476  *
49477  * Originally Released Under LGPL - original licence link has changed is not relivant.
49478  *
49479  * Fork - LGPL
49480  * <script type="text/javascript">
49481  */
49482
49483 /**
49484  * @class Roo.grid.AbstractSelectionModel
49485  * @extends Roo.util.Observable
49486  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49487  * implemented by descendant classes.  This class should not be directly instantiated.
49488  * @constructor
49489  */
49490 Roo.grid.AbstractSelectionModel = function(){
49491     this.locked = false;
49492     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49493 };
49494
49495 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49496     /** @ignore Called by the grid automatically. Do not call directly. */
49497     init : function(grid){
49498         this.grid = grid;
49499         this.initEvents();
49500     },
49501
49502     /**
49503      * Locks the selections.
49504      */
49505     lock : function(){
49506         this.locked = true;
49507     },
49508
49509     /**
49510      * Unlocks the selections.
49511      */
49512     unlock : function(){
49513         this.locked = false;
49514     },
49515
49516     /**
49517      * Returns true if the selections are locked.
49518      * @return {Boolean}
49519      */
49520     isLocked : function(){
49521         return this.locked;
49522     }
49523 });/*
49524  * Based on:
49525  * Ext JS Library 1.1.1
49526  * Copyright(c) 2006-2007, Ext JS, LLC.
49527  *
49528  * Originally Released Under LGPL - original licence link has changed is not relivant.
49529  *
49530  * Fork - LGPL
49531  * <script type="text/javascript">
49532  */
49533 /**
49534  * @extends Roo.grid.AbstractSelectionModel
49535  * @class Roo.grid.RowSelectionModel
49536  * The default SelectionModel used by {@link Roo.grid.Grid}.
49537  * It supports multiple selections and keyboard selection/navigation. 
49538  * @constructor
49539  * @param {Object} config
49540  */
49541 Roo.grid.RowSelectionModel = function(config){
49542     Roo.apply(this, config);
49543     this.selections = new Roo.util.MixedCollection(false, function(o){
49544         return o.id;
49545     });
49546
49547     this.last = false;
49548     this.lastActive = false;
49549
49550     this.addEvents({
49551         /**
49552              * @event selectionchange
49553              * Fires when the selection changes
49554              * @param {SelectionModel} this
49555              */
49556             "selectionchange" : true,
49557         /**
49558              * @event afterselectionchange
49559              * Fires after the selection changes (eg. by key press or clicking)
49560              * @param {SelectionModel} this
49561              */
49562             "afterselectionchange" : true,
49563         /**
49564              * @event beforerowselect
49565              * Fires when a row is selected being selected, return false to cancel.
49566              * @param {SelectionModel} this
49567              * @param {Number} rowIndex The selected index
49568              * @param {Boolean} keepExisting False if other selections will be cleared
49569              */
49570             "beforerowselect" : true,
49571         /**
49572              * @event rowselect
49573              * Fires when a row is selected.
49574              * @param {SelectionModel} this
49575              * @param {Number} rowIndex The selected index
49576              * @param {Roo.data.Record} r The record
49577              */
49578             "rowselect" : true,
49579         /**
49580              * @event rowdeselect
49581              * Fires when a row is deselected.
49582              * @param {SelectionModel} this
49583              * @param {Number} rowIndex The selected index
49584              */
49585         "rowdeselect" : true
49586     });
49587     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49588     this.locked = false;
49589 };
49590
49591 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49592     /**
49593      * @cfg {Boolean} singleSelect
49594      * True to allow selection of only one row at a time (defaults to false)
49595      */
49596     singleSelect : false,
49597
49598     // private
49599     initEvents : function(){
49600
49601         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49602             this.grid.on("mousedown", this.handleMouseDown, this);
49603         }else{ // allow click to work like normal
49604             this.grid.on("rowclick", this.handleDragableRowClick, this);
49605         }
49606
49607         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49608             "up" : function(e){
49609                 if(!e.shiftKey){
49610                     this.selectPrevious(e.shiftKey);
49611                 }else if(this.last !== false && this.lastActive !== false){
49612                     var last = this.last;
49613                     this.selectRange(this.last,  this.lastActive-1);
49614                     this.grid.getView().focusRow(this.lastActive);
49615                     if(last !== false){
49616                         this.last = last;
49617                     }
49618                 }else{
49619                     this.selectFirstRow();
49620                 }
49621                 this.fireEvent("afterselectionchange", this);
49622             },
49623             "down" : function(e){
49624                 if(!e.shiftKey){
49625                     this.selectNext(e.shiftKey);
49626                 }else if(this.last !== false && this.lastActive !== false){
49627                     var last = this.last;
49628                     this.selectRange(this.last,  this.lastActive+1);
49629                     this.grid.getView().focusRow(this.lastActive);
49630                     if(last !== false){
49631                         this.last = last;
49632                     }
49633                 }else{
49634                     this.selectFirstRow();
49635                 }
49636                 this.fireEvent("afterselectionchange", this);
49637             },
49638             scope: this
49639         });
49640
49641         var view = this.grid.view;
49642         view.on("refresh", this.onRefresh, this);
49643         view.on("rowupdated", this.onRowUpdated, this);
49644         view.on("rowremoved", this.onRemove, this);
49645     },
49646
49647     // private
49648     onRefresh : function(){
49649         var ds = this.grid.dataSource, i, v = this.grid.view;
49650         var s = this.selections;
49651         s.each(function(r){
49652             if((i = ds.indexOfId(r.id)) != -1){
49653                 v.onRowSelect(i);
49654             }else{
49655                 s.remove(r);
49656             }
49657         });
49658     },
49659
49660     // private
49661     onRemove : function(v, index, r){
49662         this.selections.remove(r);
49663     },
49664
49665     // private
49666     onRowUpdated : function(v, index, r){
49667         if(this.isSelected(r)){
49668             v.onRowSelect(index);
49669         }
49670     },
49671
49672     /**
49673      * Select records.
49674      * @param {Array} records The records to select
49675      * @param {Boolean} keepExisting (optional) True to keep existing selections
49676      */
49677     selectRecords : function(records, keepExisting){
49678         if(!keepExisting){
49679             this.clearSelections();
49680         }
49681         var ds = this.grid.dataSource;
49682         for(var i = 0, len = records.length; i < len; i++){
49683             this.selectRow(ds.indexOf(records[i]), true);
49684         }
49685     },
49686
49687     /**
49688      * Gets the number of selected rows.
49689      * @return {Number}
49690      */
49691     getCount : function(){
49692         return this.selections.length;
49693     },
49694
49695     /**
49696      * Selects the first row in the grid.
49697      */
49698     selectFirstRow : function(){
49699         this.selectRow(0);
49700     },
49701
49702     /**
49703      * Select the last row.
49704      * @param {Boolean} keepExisting (optional) True to keep existing selections
49705      */
49706     selectLastRow : function(keepExisting){
49707         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
49708     },
49709
49710     /**
49711      * Selects the row immediately following the last selected row.
49712      * @param {Boolean} keepExisting (optional) True to keep existing selections
49713      */
49714     selectNext : function(keepExisting){
49715         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
49716             this.selectRow(this.last+1, keepExisting);
49717             this.grid.getView().focusRow(this.last);
49718         }
49719     },
49720
49721     /**
49722      * Selects the row that precedes the last selected row.
49723      * @param {Boolean} keepExisting (optional) True to keep existing selections
49724      */
49725     selectPrevious : function(keepExisting){
49726         if(this.last){
49727             this.selectRow(this.last-1, keepExisting);
49728             this.grid.getView().focusRow(this.last);
49729         }
49730     },
49731
49732     /**
49733      * Returns the selected records
49734      * @return {Array} Array of selected records
49735      */
49736     getSelections : function(){
49737         return [].concat(this.selections.items);
49738     },
49739
49740     /**
49741      * Returns the first selected record.
49742      * @return {Record}
49743      */
49744     getSelected : function(){
49745         return this.selections.itemAt(0);
49746     },
49747
49748
49749     /**
49750      * Clears all selections.
49751      */
49752     clearSelections : function(fast){
49753         if(this.locked) return;
49754         if(fast !== true){
49755             var ds = this.grid.dataSource;
49756             var s = this.selections;
49757             s.each(function(r){
49758                 this.deselectRow(ds.indexOfId(r.id));
49759             }, this);
49760             s.clear();
49761         }else{
49762             this.selections.clear();
49763         }
49764         this.last = false;
49765     },
49766
49767
49768     /**
49769      * Selects all rows.
49770      */
49771     selectAll : function(){
49772         if(this.locked) return;
49773         this.selections.clear();
49774         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
49775             this.selectRow(i, true);
49776         }
49777     },
49778
49779     /**
49780      * Returns True if there is a selection.
49781      * @return {Boolean}
49782      */
49783     hasSelection : function(){
49784         return this.selections.length > 0;
49785     },
49786
49787     /**
49788      * Returns True if the specified row is selected.
49789      * @param {Number/Record} record The record or index of the record to check
49790      * @return {Boolean}
49791      */
49792     isSelected : function(index){
49793         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
49794         return (r && this.selections.key(r.id) ? true : false);
49795     },
49796
49797     /**
49798      * Returns True if the specified record id is selected.
49799      * @param {String} id The id of record to check
49800      * @return {Boolean}
49801      */
49802     isIdSelected : function(id){
49803         return (this.selections.key(id) ? true : false);
49804     },
49805
49806     // private
49807     handleMouseDown : function(e, t){
49808         var view = this.grid.getView(), rowIndex;
49809         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
49810             return;
49811         };
49812         if(e.shiftKey && this.last !== false){
49813             var last = this.last;
49814             this.selectRange(last, rowIndex, e.ctrlKey);
49815             this.last = last; // reset the last
49816             view.focusRow(rowIndex);
49817         }else{
49818             var isSelected = this.isSelected(rowIndex);
49819             if(e.button !== 0 && isSelected){
49820                 view.focusRow(rowIndex);
49821             }else if(e.ctrlKey && isSelected){
49822                 this.deselectRow(rowIndex);
49823             }else if(!isSelected){
49824                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
49825                 view.focusRow(rowIndex);
49826             }
49827         }
49828         this.fireEvent("afterselectionchange", this);
49829     },
49830     // private
49831     handleDragableRowClick :  function(grid, rowIndex, e) 
49832     {
49833         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
49834             this.selectRow(rowIndex, false);
49835             grid.view.focusRow(rowIndex);
49836              this.fireEvent("afterselectionchange", this);
49837         }
49838     },
49839     
49840     /**
49841      * Selects multiple rows.
49842      * @param {Array} rows Array of the indexes of the row to select
49843      * @param {Boolean} keepExisting (optional) True to keep existing selections
49844      */
49845     selectRows : function(rows, keepExisting){
49846         if(!keepExisting){
49847             this.clearSelections();
49848         }
49849         for(var i = 0, len = rows.length; i < len; i++){
49850             this.selectRow(rows[i], true);
49851         }
49852     },
49853
49854     /**
49855      * Selects a range of rows. All rows in between startRow and endRow are also selected.
49856      * @param {Number} startRow The index of the first row in the range
49857      * @param {Number} endRow The index of the last row in the range
49858      * @param {Boolean} keepExisting (optional) True to retain existing selections
49859      */
49860     selectRange : function(startRow, endRow, keepExisting){
49861         if(this.locked) return;
49862         if(!keepExisting){
49863             this.clearSelections();
49864         }
49865         if(startRow <= endRow){
49866             for(var i = startRow; i <= endRow; i++){
49867                 this.selectRow(i, true);
49868             }
49869         }else{
49870             for(var i = startRow; i >= endRow; i--){
49871                 this.selectRow(i, true);
49872             }
49873         }
49874     },
49875
49876     /**
49877      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
49878      * @param {Number} startRow The index of the first row in the range
49879      * @param {Number} endRow The index of the last row in the range
49880      */
49881     deselectRange : function(startRow, endRow, preventViewNotify){
49882         if(this.locked) return;
49883         for(var i = startRow; i <= endRow; i++){
49884             this.deselectRow(i, preventViewNotify);
49885         }
49886     },
49887
49888     /**
49889      * Selects a row.
49890      * @param {Number} row The index of the row to select
49891      * @param {Boolean} keepExisting (optional) True to keep existing selections
49892      */
49893     selectRow : function(index, keepExisting, preventViewNotify){
49894         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
49895         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
49896             if(!keepExisting || this.singleSelect){
49897                 this.clearSelections();
49898             }
49899             var r = this.grid.dataSource.getAt(index);
49900             this.selections.add(r);
49901             this.last = this.lastActive = index;
49902             if(!preventViewNotify){
49903                 this.grid.getView().onRowSelect(index);
49904             }
49905             this.fireEvent("rowselect", this, index, r);
49906             this.fireEvent("selectionchange", this);
49907         }
49908     },
49909
49910     /**
49911      * Deselects a row.
49912      * @param {Number} row The index of the row to deselect
49913      */
49914     deselectRow : function(index, preventViewNotify){
49915         if(this.locked) return;
49916         if(this.last == index){
49917             this.last = false;
49918         }
49919         if(this.lastActive == index){
49920             this.lastActive = false;
49921         }
49922         var r = this.grid.dataSource.getAt(index);
49923         this.selections.remove(r);
49924         if(!preventViewNotify){
49925             this.grid.getView().onRowDeselect(index);
49926         }
49927         this.fireEvent("rowdeselect", this, index);
49928         this.fireEvent("selectionchange", this);
49929     },
49930
49931     // private
49932     restoreLast : function(){
49933         if(this._last){
49934             this.last = this._last;
49935         }
49936     },
49937
49938     // private
49939     acceptsNav : function(row, col, cm){
49940         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49941     },
49942
49943     // private
49944     onEditorKey : function(field, e){
49945         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49946         if(k == e.TAB){
49947             e.stopEvent();
49948             ed.completeEdit();
49949             if(e.shiftKey){
49950                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49951             }else{
49952                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49953             }
49954         }else if(k == e.ENTER && !e.ctrlKey){
49955             e.stopEvent();
49956             ed.completeEdit();
49957             if(e.shiftKey){
49958                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
49959             }else{
49960                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
49961             }
49962         }else if(k == e.ESC){
49963             ed.cancelEdit();
49964         }
49965         if(newCell){
49966             g.startEditing(newCell[0], newCell[1]);
49967         }
49968     }
49969 });/*
49970  * Based on:
49971  * Ext JS Library 1.1.1
49972  * Copyright(c) 2006-2007, Ext JS, LLC.
49973  *
49974  * Originally Released Under LGPL - original licence link has changed is not relivant.
49975  *
49976  * Fork - LGPL
49977  * <script type="text/javascript">
49978  */
49979 /**
49980  * @class Roo.grid.CellSelectionModel
49981  * @extends Roo.grid.AbstractSelectionModel
49982  * This class provides the basic implementation for cell selection in a grid.
49983  * @constructor
49984  * @param {Object} config The object containing the configuration of this model.
49985  */
49986 Roo.grid.CellSelectionModel = function(config){
49987     Roo.apply(this, config);
49988
49989     this.selection = null;
49990
49991     this.addEvents({
49992         /**
49993              * @event beforerowselect
49994              * Fires before a cell is selected.
49995              * @param {SelectionModel} this
49996              * @param {Number} rowIndex The selected row index
49997              * @param {Number} colIndex The selected cell index
49998              */
49999             "beforecellselect" : true,
50000         /**
50001              * @event cellselect
50002              * Fires when a cell is selected.
50003              * @param {SelectionModel} this
50004              * @param {Number} rowIndex The selected row index
50005              * @param {Number} colIndex The selected cell index
50006              */
50007             "cellselect" : true,
50008         /**
50009              * @event selectionchange
50010              * Fires when the active selection changes.
50011              * @param {SelectionModel} this
50012              * @param {Object} selection null for no selection or an object (o) with two properties
50013                 <ul>
50014                 <li>o.record: the record object for the row the selection is in</li>
50015                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
50016                 </ul>
50017              */
50018             "selectionchange" : true
50019     });
50020     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
50021 };
50022
50023 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
50024
50025     /** @ignore */
50026     initEvents : function(){
50027         this.grid.on("mousedown", this.handleMouseDown, this);
50028         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
50029         var view = this.grid.view;
50030         view.on("refresh", this.onViewChange, this);
50031         view.on("rowupdated", this.onRowUpdated, this);
50032         view.on("beforerowremoved", this.clearSelections, this);
50033         view.on("beforerowsinserted", this.clearSelections, this);
50034         if(this.grid.isEditor){
50035             this.grid.on("beforeedit", this.beforeEdit,  this);
50036         }
50037     },
50038
50039         //private
50040     beforeEdit : function(e){
50041         this.select(e.row, e.column, false, true, e.record);
50042     },
50043
50044         //private
50045     onRowUpdated : function(v, index, r){
50046         if(this.selection && this.selection.record == r){
50047             v.onCellSelect(index, this.selection.cell[1]);
50048         }
50049     },
50050
50051         //private
50052     onViewChange : function(){
50053         this.clearSelections(true);
50054     },
50055
50056         /**
50057          * Returns the currently selected cell,.
50058          * @return {Array} The selected cell (row, column) or null if none selected.
50059          */
50060     getSelectedCell : function(){
50061         return this.selection ? this.selection.cell : null;
50062     },
50063
50064     /**
50065      * Clears all selections.
50066      * @param {Boolean} true to prevent the gridview from being notified about the change.
50067      */
50068     clearSelections : function(preventNotify){
50069         var s = this.selection;
50070         if(s){
50071             if(preventNotify !== true){
50072                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
50073             }
50074             this.selection = null;
50075             this.fireEvent("selectionchange", this, null);
50076         }
50077     },
50078
50079     /**
50080      * Returns true if there is a selection.
50081      * @return {Boolean}
50082      */
50083     hasSelection : function(){
50084         return this.selection ? true : false;
50085     },
50086
50087     /** @ignore */
50088     handleMouseDown : function(e, t){
50089         var v = this.grid.getView();
50090         if(this.isLocked()){
50091             return;
50092         };
50093         var row = v.findRowIndex(t);
50094         var cell = v.findCellIndex(t);
50095         if(row !== false && cell !== false){
50096             this.select(row, cell);
50097         }
50098     },
50099
50100     /**
50101      * Selects a cell.
50102      * @param {Number} rowIndex
50103      * @param {Number} collIndex
50104      */
50105     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
50106         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
50107             this.clearSelections();
50108             r = r || this.grid.dataSource.getAt(rowIndex);
50109             this.selection = {
50110                 record : r,
50111                 cell : [rowIndex, colIndex]
50112             };
50113             if(!preventViewNotify){
50114                 var v = this.grid.getView();
50115                 v.onCellSelect(rowIndex, colIndex);
50116                 if(preventFocus !== true){
50117                     v.focusCell(rowIndex, colIndex);
50118                 }
50119             }
50120             this.fireEvent("cellselect", this, rowIndex, colIndex);
50121             this.fireEvent("selectionchange", this, this.selection);
50122         }
50123     },
50124
50125         //private
50126     isSelectable : function(rowIndex, colIndex, cm){
50127         return !cm.isHidden(colIndex);
50128     },
50129
50130     /** @ignore */
50131     handleKeyDown : function(e){
50132         Roo.log('Cell Sel Model handleKeyDown');
50133         if(!e.isNavKeyPress()){
50134             return;
50135         }
50136         var g = this.grid, s = this.selection;
50137         if(!s){
50138             e.stopEvent();
50139             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
50140             if(cell){
50141                 this.select(cell[0], cell[1]);
50142             }
50143             return;
50144         }
50145         var sm = this;
50146         var walk = function(row, col, step){
50147             return g.walkCells(row, col, step, sm.isSelectable,  sm);
50148         };
50149         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
50150         var newCell;
50151
50152         switch(k){
50153             case e.TAB:
50154                 // handled by onEditorKey
50155                 if (g.isEditor && g.editing) {
50156                     return;
50157                 }
50158                 if(e.shiftKey){
50159                      newCell = walk(r, c-1, -1);
50160                 }else{
50161                      newCell = walk(r, c+1, 1);
50162                 }
50163              break;
50164              case e.DOWN:
50165                  newCell = walk(r+1, c, 1);
50166              break;
50167              case e.UP:
50168                  newCell = walk(r-1, c, -1);
50169              break;
50170              case e.RIGHT:
50171                  newCell = walk(r, c+1, 1);
50172              break;
50173              case e.LEFT:
50174                  newCell = walk(r, c-1, -1);
50175              break;
50176              case e.ENTER:
50177                  if(g.isEditor && !g.editing){
50178                     g.startEditing(r, c);
50179                     e.stopEvent();
50180                     return;
50181                 }
50182              break;
50183         };
50184         if(newCell){
50185             this.select(newCell[0], newCell[1]);
50186             e.stopEvent();
50187         }
50188     },
50189
50190     acceptsNav : function(row, col, cm){
50191         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50192     },
50193
50194     onEditorKey : function(field, e){
50195         
50196         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50197         ///Roo.log('onEditorKey' + k);
50198         
50199         if(k == e.TAB){
50200             if(e.shiftKey){
50201                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50202             }else{
50203                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50204             }
50205             e.stopEvent();
50206         }else if(k == e.ENTER && !e.ctrlKey){
50207             ed.completeEdit();
50208             e.stopEvent();
50209             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50210         }else if(k == e.ESC){
50211             ed.cancelEdit();
50212         }
50213         
50214         
50215         if(newCell){
50216             //Roo.log('next cell after edit');
50217             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
50218         }
50219     }
50220 });/*
50221  * Based on:
50222  * Ext JS Library 1.1.1
50223  * Copyright(c) 2006-2007, Ext JS, LLC.
50224  *
50225  * Originally Released Under LGPL - original licence link has changed is not relivant.
50226  *
50227  * Fork - LGPL
50228  * <script type="text/javascript">
50229  */
50230  
50231 /**
50232  * @class Roo.grid.EditorGrid
50233  * @extends Roo.grid.Grid
50234  * Class for creating and editable grid.
50235  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
50236  * The container MUST have some type of size defined for the grid to fill. The container will be 
50237  * automatically set to position relative if it isn't already.
50238  * @param {Object} dataSource The data model to bind to
50239  * @param {Object} colModel The column model with info about this grid's columns
50240  */
50241 Roo.grid.EditorGrid = function(container, config){
50242     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
50243     this.getGridEl().addClass("xedit-grid");
50244
50245     if(!this.selModel){
50246         this.selModel = new Roo.grid.CellSelectionModel();
50247     }
50248
50249     this.activeEditor = null;
50250
50251         this.addEvents({
50252             /**
50253              * @event beforeedit
50254              * Fires before cell editing is triggered. The edit event object has the following properties <br />
50255              * <ul style="padding:5px;padding-left:16px;">
50256              * <li>grid - This grid</li>
50257              * <li>record - The record being edited</li>
50258              * <li>field - The field name being edited</li>
50259              * <li>value - The value for the field being edited.</li>
50260              * <li>row - The grid row index</li>
50261              * <li>column - The grid column index</li>
50262              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50263              * </ul>
50264              * @param {Object} e An edit event (see above for description)
50265              */
50266             "beforeedit" : true,
50267             /**
50268              * @event afteredit
50269              * Fires after a cell is edited. <br />
50270              * <ul style="padding:5px;padding-left:16px;">
50271              * <li>grid - This grid</li>
50272              * <li>record - The record being edited</li>
50273              * <li>field - The field name being edited</li>
50274              * <li>value - The value being set</li>
50275              * <li>originalValue - The original value for the field, before the edit.</li>
50276              * <li>row - The grid row index</li>
50277              * <li>column - The grid column index</li>
50278              * </ul>
50279              * @param {Object} e An edit event (see above for description)
50280              */
50281             "afteredit" : true,
50282             /**
50283              * @event validateedit
50284              * Fires after a cell is edited, but before the value is set in the record. 
50285          * You can use this to modify the value being set in the field, Return false
50286              * to cancel the change. The edit event object has the following properties <br />
50287              * <ul style="padding:5px;padding-left:16px;">
50288          * <li>editor - This editor</li>
50289              * <li>grid - This grid</li>
50290              * <li>record - The record being edited</li>
50291              * <li>field - The field name being edited</li>
50292              * <li>value - The value being set</li>
50293              * <li>originalValue - The original value for the field, before the edit.</li>
50294              * <li>row - The grid row index</li>
50295              * <li>column - The grid column index</li>
50296              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50297              * </ul>
50298              * @param {Object} e An edit event (see above for description)
50299              */
50300             "validateedit" : true
50301         });
50302     this.on("bodyscroll", this.stopEditing,  this);
50303     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
50304 };
50305
50306 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
50307     /**
50308      * @cfg {Number} clicksToEdit
50309      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
50310      */
50311     clicksToEdit: 2,
50312
50313     // private
50314     isEditor : true,
50315     // private
50316     trackMouseOver: false, // causes very odd FF errors
50317
50318     onCellDblClick : function(g, row, col){
50319         this.startEditing(row, col);
50320     },
50321
50322     onEditComplete : function(ed, value, startValue){
50323         this.editing = false;
50324         this.activeEditor = null;
50325         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
50326         var r = ed.record;
50327         var field = this.colModel.getDataIndex(ed.col);
50328         var e = {
50329             grid: this,
50330             record: r,
50331             field: field,
50332             originalValue: startValue,
50333             value: value,
50334             row: ed.row,
50335             column: ed.col,
50336             cancel:false,
50337             editor: ed
50338         };
50339         if(String(value) !== String(startValue)){
50340             
50341             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
50342                 r.set(field, e.value);
50343                 // if we are dealing with a combo box..
50344                 // then we also set the 'name' colum to be the displayField
50345                 if (ed.field.displayField && ed.field.name) {
50346                     r.set(ed.field.name, ed.field.el.dom.value);
50347                 }
50348                 
50349                 delete e.cancel; //?? why!!!
50350                 this.fireEvent("afteredit", e);
50351             }
50352         } else {
50353             this.fireEvent("afteredit", e); // always fire it!
50354         }
50355         this.view.focusCell(ed.row, ed.col);
50356     },
50357
50358     /**
50359      * Starts editing the specified for the specified row/column
50360      * @param {Number} rowIndex
50361      * @param {Number} colIndex
50362      */
50363     startEditing : function(row, col){
50364         this.stopEditing();
50365         if(this.colModel.isCellEditable(col, row)){
50366             this.view.ensureVisible(row, col, true);
50367             var r = this.dataSource.getAt(row);
50368             var field = this.colModel.getDataIndex(col);
50369             var e = {
50370                 grid: this,
50371                 record: r,
50372                 field: field,
50373                 value: r.data[field],
50374                 row: row,
50375                 column: col,
50376                 cancel:false
50377             };
50378             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
50379                 this.editing = true;
50380                 var ed = this.colModel.getCellEditor(col, row);
50381                 
50382                 if (!ed) {
50383                     return;
50384                 }
50385                 if(!ed.rendered){
50386                     ed.render(ed.parentEl || document.body);
50387                 }
50388                 ed.field.reset();
50389                 (function(){ // complex but required for focus issues in safari, ie and opera
50390                     ed.row = row;
50391                     ed.col = col;
50392                     ed.record = r;
50393                     ed.on("complete", this.onEditComplete, this, {single: true});
50394                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
50395                     this.activeEditor = ed;
50396                     var v = r.data[field];
50397                     ed.startEdit(this.view.getCell(row, col), v);
50398                     // combo's with 'displayField and name set
50399                     if (ed.field.displayField && ed.field.name) {
50400                         ed.field.el.dom.value = r.data[ed.field.name];
50401                     }
50402                     
50403                     
50404                 }).defer(50, this);
50405             }
50406         }
50407     },
50408         
50409     /**
50410      * Stops any active editing
50411      */
50412     stopEditing : function(){
50413         if(this.activeEditor){
50414             this.activeEditor.completeEdit();
50415         }
50416         this.activeEditor = null;
50417     }
50418 });/*
50419  * Based on:
50420  * Ext JS Library 1.1.1
50421  * Copyright(c) 2006-2007, Ext JS, LLC.
50422  *
50423  * Originally Released Under LGPL - original licence link has changed is not relivant.
50424  *
50425  * Fork - LGPL
50426  * <script type="text/javascript">
50427  */
50428
50429 // private - not really -- you end up using it !
50430 // This is a support class used internally by the Grid components
50431
50432 /**
50433  * @class Roo.grid.GridEditor
50434  * @extends Roo.Editor
50435  * Class for creating and editable grid elements.
50436  * @param {Object} config any settings (must include field)
50437  */
50438 Roo.grid.GridEditor = function(field, config){
50439     if (!config && field.field) {
50440         config = field;
50441         field = Roo.factory(config.field, Roo.form);
50442     }
50443     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50444     field.monitorTab = false;
50445 };
50446
50447 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50448     
50449     /**
50450      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50451      */
50452     
50453     alignment: "tl-tl",
50454     autoSize: "width",
50455     hideEl : false,
50456     cls: "x-small-editor x-grid-editor",
50457     shim:false,
50458     shadow:"frame"
50459 });/*
50460  * Based on:
50461  * Ext JS Library 1.1.1
50462  * Copyright(c) 2006-2007, Ext JS, LLC.
50463  *
50464  * Originally Released Under LGPL - original licence link has changed is not relivant.
50465  *
50466  * Fork - LGPL
50467  * <script type="text/javascript">
50468  */
50469   
50470
50471   
50472 Roo.grid.PropertyRecord = Roo.data.Record.create([
50473     {name:'name',type:'string'},  'value'
50474 ]);
50475
50476
50477 Roo.grid.PropertyStore = function(grid, source){
50478     this.grid = grid;
50479     this.store = new Roo.data.Store({
50480         recordType : Roo.grid.PropertyRecord
50481     });
50482     this.store.on('update', this.onUpdate,  this);
50483     if(source){
50484         this.setSource(source);
50485     }
50486     Roo.grid.PropertyStore.superclass.constructor.call(this);
50487 };
50488
50489
50490
50491 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50492     setSource : function(o){
50493         this.source = o;
50494         this.store.removeAll();
50495         var data = [];
50496         for(var k in o){
50497             if(this.isEditableValue(o[k])){
50498                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50499             }
50500         }
50501         this.store.loadRecords({records: data}, {}, true);
50502     },
50503
50504     onUpdate : function(ds, record, type){
50505         if(type == Roo.data.Record.EDIT){
50506             var v = record.data['value'];
50507             var oldValue = record.modified['value'];
50508             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50509                 this.source[record.id] = v;
50510                 record.commit();
50511                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50512             }else{
50513                 record.reject();
50514             }
50515         }
50516     },
50517
50518     getProperty : function(row){
50519        return this.store.getAt(row);
50520     },
50521
50522     isEditableValue: function(val){
50523         if(val && val instanceof Date){
50524             return true;
50525         }else if(typeof val == 'object' || typeof val == 'function'){
50526             return false;
50527         }
50528         return true;
50529     },
50530
50531     setValue : function(prop, value){
50532         this.source[prop] = value;
50533         this.store.getById(prop).set('value', value);
50534     },
50535
50536     getSource : function(){
50537         return this.source;
50538     }
50539 });
50540
50541 Roo.grid.PropertyColumnModel = function(grid, store){
50542     this.grid = grid;
50543     var g = Roo.grid;
50544     g.PropertyColumnModel.superclass.constructor.call(this, [
50545         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50546         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50547     ]);
50548     this.store = store;
50549     this.bselect = Roo.DomHelper.append(document.body, {
50550         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50551             {tag: 'option', value: 'true', html: 'true'},
50552             {tag: 'option', value: 'false', html: 'false'}
50553         ]
50554     });
50555     Roo.id(this.bselect);
50556     var f = Roo.form;
50557     this.editors = {
50558         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50559         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50560         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50561         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50562         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50563     };
50564     this.renderCellDelegate = this.renderCell.createDelegate(this);
50565     this.renderPropDelegate = this.renderProp.createDelegate(this);
50566 };
50567
50568 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50569     
50570     
50571     nameText : 'Name',
50572     valueText : 'Value',
50573     
50574     dateFormat : 'm/j/Y',
50575     
50576     
50577     renderDate : function(dateVal){
50578         return dateVal.dateFormat(this.dateFormat);
50579     },
50580
50581     renderBool : function(bVal){
50582         return bVal ? 'true' : 'false';
50583     },
50584
50585     isCellEditable : function(colIndex, rowIndex){
50586         return colIndex == 1;
50587     },
50588
50589     getRenderer : function(col){
50590         return col == 1 ?
50591             this.renderCellDelegate : this.renderPropDelegate;
50592     },
50593
50594     renderProp : function(v){
50595         return this.getPropertyName(v);
50596     },
50597
50598     renderCell : function(val){
50599         var rv = val;
50600         if(val instanceof Date){
50601             rv = this.renderDate(val);
50602         }else if(typeof val == 'boolean'){
50603             rv = this.renderBool(val);
50604         }
50605         return Roo.util.Format.htmlEncode(rv);
50606     },
50607
50608     getPropertyName : function(name){
50609         var pn = this.grid.propertyNames;
50610         return pn && pn[name] ? pn[name] : name;
50611     },
50612
50613     getCellEditor : function(colIndex, rowIndex){
50614         var p = this.store.getProperty(rowIndex);
50615         var n = p.data['name'], val = p.data['value'];
50616         
50617         if(typeof(this.grid.customEditors[n]) == 'string'){
50618             return this.editors[this.grid.customEditors[n]];
50619         }
50620         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50621             return this.grid.customEditors[n];
50622         }
50623         if(val instanceof Date){
50624             return this.editors['date'];
50625         }else if(typeof val == 'number'){
50626             return this.editors['number'];
50627         }else if(typeof val == 'boolean'){
50628             return this.editors['boolean'];
50629         }else{
50630             return this.editors['string'];
50631         }
50632     }
50633 });
50634
50635 /**
50636  * @class Roo.grid.PropertyGrid
50637  * @extends Roo.grid.EditorGrid
50638  * This class represents the  interface of a component based property grid control.
50639  * <br><br>Usage:<pre><code>
50640  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50641       
50642  });
50643  // set any options
50644  grid.render();
50645  * </code></pre>
50646   
50647  * @constructor
50648  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50649  * The container MUST have some type of size defined for the grid to fill. The container will be
50650  * automatically set to position relative if it isn't already.
50651  * @param {Object} config A config object that sets properties on this grid.
50652  */
50653 Roo.grid.PropertyGrid = function(container, config){
50654     config = config || {};
50655     var store = new Roo.grid.PropertyStore(this);
50656     this.store = store;
50657     var cm = new Roo.grid.PropertyColumnModel(this, store);
50658     store.store.sort('name', 'ASC');
50659     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50660         ds: store.store,
50661         cm: cm,
50662         enableColLock:false,
50663         enableColumnMove:false,
50664         stripeRows:false,
50665         trackMouseOver: false,
50666         clicksToEdit:1
50667     }, config));
50668     this.getGridEl().addClass('x-props-grid');
50669     this.lastEditRow = null;
50670     this.on('columnresize', this.onColumnResize, this);
50671     this.addEvents({
50672          /**
50673              * @event beforepropertychange
50674              * Fires before a property changes (return false to stop?)
50675              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50676              * @param {String} id Record Id
50677              * @param {String} newval New Value
50678          * @param {String} oldval Old Value
50679              */
50680         "beforepropertychange": true,
50681         /**
50682              * @event propertychange
50683              * Fires after a property changes
50684              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50685              * @param {String} id Record Id
50686              * @param {String} newval New Value
50687          * @param {String} oldval Old Value
50688              */
50689         "propertychange": true
50690     });
50691     this.customEditors = this.customEditors || {};
50692 };
50693 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
50694     
50695      /**
50696      * @cfg {Object} customEditors map of colnames=> custom editors.
50697      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
50698      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
50699      * false disables editing of the field.
50700          */
50701     
50702       /**
50703      * @cfg {Object} propertyNames map of property Names to their displayed value
50704          */
50705     
50706     render : function(){
50707         Roo.grid.PropertyGrid.superclass.render.call(this);
50708         this.autoSize.defer(100, this);
50709     },
50710
50711     autoSize : function(){
50712         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
50713         if(this.view){
50714             this.view.fitColumns();
50715         }
50716     },
50717
50718     onColumnResize : function(){
50719         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
50720         this.autoSize();
50721     },
50722     /**
50723      * Sets the data for the Grid
50724      * accepts a Key => Value object of all the elements avaiable.
50725      * @param {Object} data  to appear in grid.
50726      */
50727     setSource : function(source){
50728         this.store.setSource(source);
50729         //this.autoSize();
50730     },
50731     /**
50732      * Gets all the data from the grid.
50733      * @return {Object} data  data stored in grid
50734      */
50735     getSource : function(){
50736         return this.store.getSource();
50737     }
50738 });/*
50739  * Based on:
50740  * Ext JS Library 1.1.1
50741  * Copyright(c) 2006-2007, Ext JS, LLC.
50742  *
50743  * Originally Released Under LGPL - original licence link has changed is not relivant.
50744  *
50745  * Fork - LGPL
50746  * <script type="text/javascript">
50747  */
50748  
50749 /**
50750  * @class Roo.LoadMask
50751  * A simple utility class for generically masking elements while loading data.  If the element being masked has
50752  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
50753  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
50754  * element's UpdateManager load indicator and will be destroyed after the initial load.
50755  * @constructor
50756  * Create a new LoadMask
50757  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
50758  * @param {Object} config The config object
50759  */
50760 Roo.LoadMask = function(el, config){
50761     this.el = Roo.get(el);
50762     Roo.apply(this, config);
50763     if(this.store){
50764         this.store.on('beforeload', this.onBeforeLoad, this);
50765         this.store.on('load', this.onLoad, this);
50766         this.store.on('loadexception', this.onLoad, this);
50767         this.removeMask = false;
50768     }else{
50769         var um = this.el.getUpdateManager();
50770         um.showLoadIndicator = false; // disable the default indicator
50771         um.on('beforeupdate', this.onBeforeLoad, this);
50772         um.on('update', this.onLoad, this);
50773         um.on('failure', this.onLoad, this);
50774         this.removeMask = true;
50775     }
50776 };
50777
50778 Roo.LoadMask.prototype = {
50779     /**
50780      * @cfg {Boolean} removeMask
50781      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
50782      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
50783      */
50784     /**
50785      * @cfg {String} msg
50786      * The text to display in a centered loading message box (defaults to 'Loading...')
50787      */
50788     msg : 'Loading...',
50789     /**
50790      * @cfg {String} msgCls
50791      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
50792      */
50793     msgCls : 'x-mask-loading',
50794
50795     /**
50796      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
50797      * @type Boolean
50798      */
50799     disabled: false,
50800
50801     /**
50802      * Disables the mask to prevent it from being displayed
50803      */
50804     disable : function(){
50805        this.disabled = true;
50806     },
50807
50808     /**
50809      * Enables the mask so that it can be displayed
50810      */
50811     enable : function(){
50812         this.disabled = false;
50813     },
50814
50815     // private
50816     onLoad : function(){
50817         this.el.unmask(this.removeMask);
50818     },
50819
50820     // private
50821     onBeforeLoad : function(){
50822         if(!this.disabled){
50823             this.el.mask(this.msg, this.msgCls);
50824         }
50825     },
50826
50827     // private
50828     destroy : function(){
50829         if(this.store){
50830             this.store.un('beforeload', this.onBeforeLoad, this);
50831             this.store.un('load', this.onLoad, this);
50832             this.store.un('loadexception', this.onLoad, this);
50833         }else{
50834             var um = this.el.getUpdateManager();
50835             um.un('beforeupdate', this.onBeforeLoad, this);
50836             um.un('update', this.onLoad, this);
50837             um.un('failure', this.onLoad, this);
50838         }
50839     }
50840 };/*
50841  * Based on:
50842  * Ext JS Library 1.1.1
50843  * Copyright(c) 2006-2007, Ext JS, LLC.
50844  *
50845  * Originally Released Under LGPL - original licence link has changed is not relivant.
50846  *
50847  * Fork - LGPL
50848  * <script type="text/javascript">
50849  */
50850 Roo.XTemplate = function(){
50851     Roo.XTemplate.superclass.constructor.apply(this, arguments);
50852     var s = this.html;
50853
50854     s = ['<tpl>', s, '</tpl>'].join('');
50855
50856     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
50857
50858     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
50859     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
50860     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
50861     var m, id = 0;
50862     var tpls = [];
50863
50864     while(m = s.match(re)){
50865        var m2 = m[0].match(nameRe);
50866        var m3 = m[0].match(ifRe);
50867        var m4 = m[0].match(execRe);
50868        var exp = null, fn = null, exec = null;
50869        var name = m2 && m2[1] ? m2[1] : '';
50870        if(m3){
50871            exp = m3 && m3[1] ? m3[1] : null;
50872            if(exp){
50873                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
50874            }
50875        }
50876        if(m4){
50877            exp = m4 && m4[1] ? m4[1] : null;
50878            if(exp){
50879                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
50880            }
50881        }
50882        if(name){
50883            switch(name){
50884                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
50885                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
50886                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
50887            }
50888        }
50889        tpls.push({
50890             id: id,
50891             target: name,
50892             exec: exec,
50893             test: fn,
50894             body: m[1]||''
50895         });
50896        s = s.replace(m[0], '{xtpl'+ id + '}');
50897        ++id;
50898     }
50899     for(var i = tpls.length-1; i >= 0; --i){
50900         this.compileTpl(tpls[i]);
50901     }
50902     this.master = tpls[tpls.length-1];
50903     this.tpls = tpls;
50904 };
50905 Roo.extend(Roo.XTemplate, Roo.Template, {
50906
50907     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
50908
50909     applySubTemplate : function(id, values, parent){
50910         var t = this.tpls[id];
50911         if(t.test && !t.test.call(this, values, parent)){
50912             return '';
50913         }
50914         if(t.exec && t.exec.call(this, values, parent)){
50915             return '';
50916         }
50917         var vs = t.target ? t.target.call(this, values, parent) : values;
50918         parent = t.target ? values : parent;
50919         if(t.target && vs instanceof Array){
50920             var buf = [];
50921             for(var i = 0, len = vs.length; i < len; i++){
50922                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
50923             }
50924             return buf.join('');
50925         }
50926         return t.compiled.call(this, vs, parent);
50927     },
50928
50929     compileTpl : function(tpl){
50930         var fm = Roo.util.Format;
50931         var useF = this.disableFormats !== true;
50932         var sep = Roo.isGecko ? "+" : ",";
50933         var fn = function(m, name, format, args){
50934             if(name.substr(0, 4) == 'xtpl'){
50935                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
50936             }
50937             var v;
50938             if(name.indexOf('.') != -1){
50939                 v = name;
50940             }else{
50941                 v = "values['" + name + "']";
50942             }
50943             if(format && useF){
50944                 args = args ? ',' + args : "";
50945                 if(format.substr(0, 5) != "this."){
50946                     format = "fm." + format + '(';
50947                 }else{
50948                     format = 'this.call("'+ format.substr(5) + '", ';
50949                     args = ", values";
50950                 }
50951             }else{
50952                 args= ''; format = "("+v+" === undefined ? '' : ";
50953             }
50954             return "'"+ sep + format + v + args + ")"+sep+"'";
50955         };
50956         var body;
50957         // branched to use + in gecko and [].join() in others
50958         if(Roo.isGecko){
50959             body = "tpl.compiled = function(values, parent){ return '" +
50960                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
50961                     "';};";
50962         }else{
50963             body = ["tpl.compiled = function(values, parent){ return ['"];
50964             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
50965             body.push("'].join('');};");
50966             body = body.join('');
50967         }
50968         /** eval:var:zzzzzzz */
50969         eval(body);
50970         return this;
50971     },
50972
50973     applyTemplate : function(values){
50974         return this.master.compiled.call(this, values, {});
50975         var s = this.subs;
50976     },
50977
50978     apply : function(){
50979         return this.applyTemplate.apply(this, arguments);
50980     },
50981
50982     compile : function(){return this;}
50983 });
50984
50985 Roo.XTemplate.from = function(el){
50986     el = Roo.getDom(el);
50987     return new Roo.XTemplate(el.value || el.innerHTML);
50988 };/*
50989  * Original code for Roojs - LGPL
50990  * <script type="text/javascript">
50991  */
50992  
50993 /**
50994  * @class Roo.XComponent
50995  * A delayed Element creator...
50996  * Or a way to group chunks of interface together.
50997  * 
50998  * Mypart.xyx = new Roo.XComponent({
50999
51000     parent : 'Mypart.xyz', // empty == document.element.!!
51001     order : '001',
51002     name : 'xxxx'
51003     region : 'xxxx'
51004     disabled : function() {} 
51005      
51006     tree : function() { // return an tree of xtype declared components
51007         var MODULE = this;
51008         return 
51009         {
51010             xtype : 'NestedLayoutPanel',
51011             // technicall
51012         }
51013      ]
51014  *})
51015  *
51016  *
51017  * It can be used to build a big heiracy, with parent etc.
51018  * or you can just use this to render a single compoent to a dom element
51019  * MYPART.render(Roo.Element | String(id) | dom_element )
51020  * 
51021  * @extends Roo.util.Observable
51022  * @constructor
51023  * @param cfg {Object} configuration of component
51024  * 
51025  */
51026 Roo.XComponent = function(cfg) {
51027     Roo.apply(this, cfg);
51028     this.addEvents({ 
51029         /**
51030              * @event built
51031              * Fires when this the componnt is built
51032              * @param {Roo.XComponent} c the component
51033              */
51034         'built' : true,
51035         /**
51036              * @event buildcomplete
51037              * Fires on the top level element when all elements have been built
51038              * @param {Roo.XComponent} c the top level component.
51039          */
51040         'buildcomplete' : true
51041         
51042     });
51043     
51044     Roo.XComponent.register(this);
51045     this.modules = false;
51046     this.el = false; // where the layout goes..
51047     
51048     
51049 }
51050 Roo.extend(Roo.XComponent, Roo.util.Observable, {
51051     /**
51052      * @property el
51053      * The created element (with Roo.factory())
51054      * @type {Roo.Layout}
51055      */
51056     el  : false,
51057     
51058     /**
51059      * @property el
51060      * for BC  - use el in new code
51061      * @type {Roo.Layout}
51062      */
51063     panel : false,
51064     
51065     /**
51066      * @property layout
51067      * for BC  - use el in new code
51068      * @type {Roo.Layout}
51069      */
51070     layout : false,
51071     
51072      /**
51073      * @cfg {Function|boolean} disabled
51074      * If this module is disabled by some rule, return true from the funtion
51075      */
51076     disabled : false,
51077     
51078     /**
51079      * @cfg {String} parent 
51080      * Name of parent element which it get xtype added to..
51081      */
51082     parent: false,
51083     
51084     /**
51085      * @cfg {String} order
51086      * Used to set the order in which elements are created (usefull for multiple tabs)
51087      */
51088     
51089     order : false,
51090     /**
51091      * @cfg {String} name
51092      * String to display while loading.
51093      */
51094     name : false,
51095     /**
51096      * @cfg {String} region
51097      * Region to render component to (defaults to center)
51098      */
51099     region : 'center',
51100     
51101     /**
51102      * @cfg {Array} items
51103      * A single item array - the first element is the root of the tree..
51104      * It's done this way to stay compatible with the Xtype system...
51105      */
51106     items : false,
51107     
51108     
51109      /**
51110      * render
51111      * render element to dom or tree
51112      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
51113      */
51114     
51115     render : function(el)
51116     {
51117         
51118         if (!this.parent) {
51119             
51120             el = el ? Roo.get(el) : false;
51121             
51122             
51123             // it's a top level one..
51124             this.parent =  {
51125                 el : new Ext.BorderLayout(el || document.body, {
51126                 
51127                      center: {
51128                          titlebar: false,
51129                          autoScroll:false,
51130                          closeOnTab: true,
51131                          tabPosition: 'top',
51132                           //resizeTabs: true,
51133                          alwaysShowTabs: el ? false :  true,
51134                          minTabWidth: 140
51135                      }
51136                  })
51137             }
51138         }
51139             
51140         var tree = this.tree();
51141         tree.region = tree.region || this.region;
51142         this.el = this.parent.el.addxtype(tree);
51143         this.fireEvent('built', this);
51144         
51145         this.panel = this.el;
51146         this.layout = this.panel.layout;    
51147          
51148     }
51149     
51150      
51151      
51152     
51153 });
51154
51155 Roo.apply(Roo.XComponent, {
51156     
51157     /**
51158      * @property  buildCompleted
51159      * True when the builder has completed building the interface.
51160      * @type Boolean
51161      */
51162     buildCompleted : false,
51163      
51164     /**
51165      * @property  topModule
51166      * the upper most module - uses document.element as it's constructor.
51167      * @type Object
51168      */
51169      
51170     topModule  : false,
51171       
51172     /**
51173      * @property  modules
51174      * array of modules to be created by registration system.
51175      * @type Roo.XComponent
51176      */
51177     
51178     modules : [],
51179       
51180     
51181     /**
51182      * Register components to be built later.
51183      *
51184      * This solves the following issues
51185      * - Building is not done on page load, but after an authentication process has occured.
51186      * - Interface elements are registered on page load
51187      * - Parent Interface elements may not be loaded before child, so this handles that..
51188      * 
51189      *
51190      * example:
51191      * 
51192      * MyApp.register({
51193           order : '000001',
51194           module : 'Pman.Tab.projectMgr',
51195           region : 'center',
51196           parent : 'Pman.layout',
51197           disabled : false,  // or use a function..
51198         })
51199      
51200      * * @param {Object} details about module
51201      */
51202     register : function(obj) {
51203         this.modules.push(obj);
51204          
51205     },
51206     /**
51207      * convert a string to an object..
51208      * 
51209      */
51210     
51211     toObject : function(str)
51212     {
51213         if (!str || typeof(str) == 'object') {
51214             return str;
51215         }
51216         var ar = str.split('.');
51217         var rt, o;
51218         rt = ar.shift();
51219             /** eval:var:o */
51220         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
51221         if (o === false) {
51222             throw "Module not found : " + str;
51223         }
51224         Roo.each(ar, function(e) {
51225             if (typeof(o[e]) == 'undefined') {
51226                 throw "Module not found : " + str;
51227             }
51228             o = o[e];
51229         });
51230         return o;
51231         
51232     },
51233     
51234     
51235     /**
51236      * move modules into their correct place in the tree..
51237      * 
51238      */
51239     preBuild : function ()
51240     {
51241         
51242         Roo.each(this.modules , function (obj)
51243         {
51244             obj.parent = this.toObject(obj.parent);
51245             
51246             if (!obj.parent) {
51247                 this.topModule = obj;
51248                 return;
51249             }
51250             
51251             if (!obj.parent.modules) {
51252                 obj.parent.modules = new Roo.util.MixedCollection(false, 
51253                     function(o) { return o.order + '' }
51254                 );
51255             }
51256             
51257             obj.parent.modules.add(obj);
51258         }, this);
51259     },
51260     
51261      /**
51262      * make a list of modules to build.
51263      * @return {Array} list of modules. 
51264      */ 
51265     
51266     buildOrder : function()
51267     {
51268         var _this = this;
51269         var cmp = function(a,b) {   
51270             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
51271         };
51272         
51273         if (!this.topModule || !this.topModule.modules) {
51274             throw "No top level modules to build";
51275         }
51276        
51277         // make a flat list in order of modules to build.
51278         var mods = [ this.topModule ];
51279         
51280         
51281         // add modules to their parents..
51282         var addMod = function(m) {
51283            // Roo.debug && Roo.log(m.modKey);
51284             
51285             mods.push(m);
51286             if (m.modules) {
51287                 m.modules.keySort('ASC',  cmp );
51288                 m.modules.each(addMod);
51289             }
51290             // not sure if this is used any more..
51291             if (m.finalize) {
51292                 m.finalize.name = m.name + " (clean up) ";
51293                 mods.push(m.finalize);
51294             }
51295             
51296         }
51297         this.topModule.modules.keySort('ASC',  cmp );
51298         this.topModule.modules.each(addMod);
51299         return mods;
51300     },
51301     
51302      /**
51303      * Build the registered modules.
51304      * @param {Object} parent element.
51305      * @param {Function} optional method to call after module has been added.
51306      * 
51307      */ 
51308    
51309     build : function() 
51310     {
51311         
51312         this.preBuild();
51313         var mods = this.buildOrder();
51314       
51315         //this.allmods = mods;
51316         //Roo.debug && Roo.log(mods);
51317         //return;
51318         if (!mods.length) { // should not happen
51319             throw "NO modules!!!";
51320         }
51321         
51322         
51323         
51324         // flash it up as modal - so we store the mask!?
51325         Roo.MessageBox.show({ title: 'loading' });
51326         Roo.MessageBox.show({
51327            title: "Please wait...",
51328            msg: "Building Interface...",
51329            width:450,
51330            progress:true,
51331            closable:false,
51332            modal: false
51333           
51334         });
51335         var total = mods.length;
51336         
51337         var _this = this;
51338         var progressRun = function() {
51339             if (!mods.length) {
51340                 Roo.debug && Roo.log('hide?');
51341                 Roo.MessageBox.hide();
51342                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
51343                 return flase;    
51344             }
51345             
51346             var m = mods.shift();
51347             
51348             
51349             Roo.debug && Roo.log(m);
51350             // not sure if this is supported any more.. - modules that are are just function
51351             if (typeof(m) == 'function') { 
51352                 m.call(this);
51353                 return progressRun.defer(10, _this);
51354             } 
51355             
51356             
51357             
51358             Roo.MessageBox.updateProgress(
51359                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
51360                     " of " + total + 
51361                     (m.name ? (' - ' + m.name) : '')
51362                     );
51363             
51364          
51365             // is the module disabled?
51366             var disabled = (typeof(m.disabled) == 'function') ?
51367                 m.disabled.call(m.module.disabled) : m.disabled;    
51368             
51369             
51370             if (disabled) {
51371                 return progressRun(); // we do not update the display!
51372             }
51373             
51374             // now build 
51375             
51376             m.render();
51377             // it's 10 on top level, and 1 on others??? why...
51378             return progressRun.defer(10, _this);
51379              
51380         }
51381         progressRun.defer(1, _this);
51382      
51383         
51384         
51385     }
51386     
51387      
51388    
51389     
51390     
51391 });
51392  //<script type="text/javascript">
51393
51394
51395 /**
51396  * @class Roo.Login
51397  * @extends Roo.LayoutDialog
51398  * A generic Login Dialog..... - only one needed in theory!?!?
51399  *
51400  * Fires XComponent builder on success...
51401  * 
51402  * Sends 
51403  *    username,password, lang = for login actions.
51404  *    check = 1 for periodic checking that sesion is valid.
51405  *    passwordRequest = email request password
51406  *    logout = 1 = to logout
51407  * 
51408  * Affects: (this id="????" elements)
51409  *   loading  (removed) (used to indicate application is loading)
51410  *   loading-mask (hides) (used to hide application when it's building loading)
51411  *   
51412  * 
51413  * Usage: 
51414  *    
51415  * 
51416  * Myapp.login = Roo.Login({
51417      url: xxxx,
51418    
51419      realm : 'Myapp', 
51420      
51421      
51422      method : 'POST',
51423      
51424      
51425      * 
51426  })
51427  * 
51428  * 
51429  * 
51430  **/
51431  
51432 Roo.Login = function(cfg)
51433 {
51434     this.addEvents({
51435         'refreshed' : true
51436     });
51437     
51438     Roo.apply(this,cfg);
51439     
51440     Roo.onReady(function() {
51441         this.onLoad();
51442     }, this);
51443     // call parent..
51444     
51445    
51446     Roo.Login.superclass.constructor.call(this, this);
51447     //this.addxtype(this.items[0]);
51448     
51449     
51450 }
51451
51452
51453 Roo.extend(Roo.Login, Roo.LayoutDialog, {
51454     
51455     /**
51456      * @cfg {String} method
51457      * Method used to query for login details.
51458      */
51459     
51460     method : 'POST',
51461     /**
51462      * @cfg {String} url
51463      * URL to query login data. - eg. baseURL + '/Login.php'
51464      */
51465     url : '',
51466     
51467     /**
51468      * @property user
51469      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51470      * @type {Object} 
51471      */
51472     user : false,
51473     /**
51474      * @property checkFails
51475      * Number of times we have attempted to get authentication check, and failed.
51476      * @type {Number} 
51477      */
51478     checkFails : 0,
51479       /**
51480      * @property intervalID
51481      * The window interval that does the constant login checking.
51482      * @type {Number} 
51483      */
51484     intervalID : 0,
51485     
51486     
51487     onLoad : function() // called on page load...
51488     {
51489         // load 
51490          
51491         if (Roo.get('loading')) { // clear any loading indicator..
51492             Roo.get('loading').remove();
51493         }
51494         
51495         //this.switchLang('en'); // set the language to english..
51496        
51497         this.check({
51498             success:  function(response, opts)  {  // check successfull...
51499             
51500                 var res = this.processResponse(response);
51501                 this.checkFails =0;
51502                 if (!res.success) { // error!
51503                     this.checkFails = 5;
51504                     //console.log('call failure');
51505                     return this.failure(response,opts);
51506                 }
51507                 
51508                 if (!res.data.id) { // id=0 == login failure.
51509                     return this.show();
51510                 }
51511                 
51512                               
51513                         //console.log(success);
51514                 this.fillAuth(res.data);   
51515                 this.checkFails =0;
51516                 Roo.XComponent.build();
51517             },
51518             failure : this.show
51519         });
51520         
51521     }, 
51522     
51523     
51524     check: function(cfg) // called every so often to refresh cookie etc..
51525     {
51526         if (cfg.again) { // could be undefined..
51527             this.checkFails++;
51528         } else {
51529             this.checkFails = 0;
51530         }
51531         var _this = this;
51532         if (this.sending) {
51533             if ( this.checkFails > 4) {
51534                 Roo.MessageBox.alert("Error",  
51535                     "Error getting authentication status. - try reloading, or wait a while", function() {
51536                         _this.sending = false;
51537                     }); 
51538                 return;
51539             }
51540             cfg.again = true;
51541             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51542             return;
51543         }
51544         this.sending = true;
51545         
51546         Roo.Ajax.request({  
51547             url: this.url,
51548             params: {
51549                 getAuthUser: true
51550             },  
51551             method: this.method,
51552             success:  cfg.success || this.success,
51553             failure : cfg.failure || this.failure,
51554             scope : this,
51555             callCfg : cfg
51556               
51557         });  
51558     }, 
51559     
51560     
51561     logout: function()
51562     {
51563         window.onbeforeunload = function() { }; // false does not work for IE..
51564         this.user = false;
51565         var _this = this;
51566         
51567         Roo.Ajax.request({  
51568             url: this.url,
51569             params: {
51570                 logout: 1
51571             },  
51572             method: 'GET',
51573             failure : function() {
51574                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51575                     document.location = document.location.toString() + '?ts=' + Math.random();
51576                 });
51577                 
51578             },
51579             success : function() {
51580                 _this.user = false;
51581                 this.checkFails =0;
51582                 // fixme..
51583                 document.location = document.location.toString() + '?ts=' + Math.random();
51584             }
51585               
51586               
51587         }); 
51588     },
51589     
51590     processResponse : function (response)
51591     {
51592         var res = '';
51593         try {
51594             res = Roo.decode(response.responseText);
51595             // oops...
51596             if (typeof(res) != 'object') {
51597                 res = { success : false, errorMsg : res, errors : true };
51598             }
51599             if (typeof(res.success) == 'undefined') {
51600                 res.success = false;
51601             }
51602             
51603         } catch(e) {
51604             res = { success : false,  errorMsg : response.responseText, errors : true };
51605         }
51606         return res;
51607     },
51608     
51609     success : function(response, opts)  // check successfull...
51610     {  
51611         this.sending = false;
51612         var res = this.processResponse(response);
51613         if (!res.success) {
51614             return this.failure(response, opts);
51615         }
51616         if (!res.data || !res.data.id) {
51617             return this.failure(response,opts);
51618         }
51619         //console.log(res);
51620         this.fillAuth(res.data);
51621         
51622         this.checkFails =0;
51623         
51624     },
51625     
51626     
51627     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
51628     {
51629         this.authUser = -1;
51630         this.sending = false;
51631         var res = this.processResponse(response);
51632         //console.log(res);
51633         if ( this.checkFails > 2) {
51634         
51635             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
51636                 "Error getting authentication status. - try reloading"); 
51637             return;
51638         }
51639         opts.callCfg.again = true;
51640         this.check.defer(1000, this, [ opts.callCfg ]);
51641         return;  
51642     },
51643     
51644     
51645     
51646     fillAuth: function(au) {
51647         this.startAuthCheck();
51648         this.authUserId = au.id;
51649         this.authUser = au;
51650         this.lastChecked = new Date();
51651         this.fireEvent('refreshed', au);
51652         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
51653         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
51654         au.lang = au.lang || 'en';
51655         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
51656         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
51657         this.switchLang(au.lang );
51658         
51659      
51660         // open system... - -on setyp..
51661         if (this.authUserId  < 0) {
51662             Roo.MessageBox.alert("Warning", 
51663                 "This is an open system - please set up a admin user with a password.");  
51664         }
51665          
51666         //Pman.onload(); // which should do nothing if it's a re-auth result...
51667         
51668              
51669     },
51670     
51671     startAuthCheck : function() // starter for timeout checking..
51672     {
51673         if (this.intervalID) { // timer already in place...
51674             return false;
51675         }
51676         var _this = this;
51677         this.intervalID =  window.setInterval(function() {
51678               _this.check(false);
51679             }, 120000); // every 120 secs = 2mins..
51680         
51681         
51682     },
51683          
51684     
51685     switchLang : function (lang) 
51686     {
51687         _T = typeof(_T) == 'undefined' ? false : _T;
51688           if (!_T || !lang.length) {
51689             return;
51690         }
51691         
51692         if (!_T && lang != 'en') {
51693             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51694             return;
51695         }
51696         
51697         if (typeof(_T.en) == 'undefined') {
51698             _T.en = {};
51699             Roo.apply(_T.en, _T);
51700         }
51701         
51702         if (typeof(_T[lang]) == 'undefined') {
51703             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51704             return;
51705         }
51706         
51707         
51708         Roo.apply(_T, _T[lang]);
51709         // just need to set the text values for everything...
51710         var _this = this;
51711         /* this will not work ...
51712         if (this.form) { 
51713             
51714                
51715             function formLabel(name, val) {
51716                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
51717             }
51718             
51719             formLabel('password', "Password"+':');
51720             formLabel('username', "Email Address"+':');
51721             formLabel('lang', "Language"+':');
51722             this.dialog.setTitle("Login");
51723             this.dialog.buttons[0].setText("Forgot Password");
51724             this.dialog.buttons[1].setText("Login");
51725         }
51726         */
51727         
51728         
51729     },
51730     
51731     
51732     title: "Login",
51733     modal: true,
51734     width:  350,
51735     //height: 230,
51736     height: 180,
51737     shadow: true,
51738     minWidth:200,
51739     minHeight:180,
51740     //proxyDrag: true,
51741     closable: false,
51742     draggable: false,
51743     collapsible: false,
51744     resizable: false,
51745     center: {  // needed??
51746         autoScroll:false,
51747         titlebar: false,
51748        // tabPosition: 'top',
51749         hideTabs: true,
51750         closeOnTab: true,
51751         alwaysShowTabs: false
51752     } ,
51753     listeners : {
51754         
51755         show  : function(dlg)
51756         {
51757             //console.log(this);
51758             this.form = this.layout.getRegion('center').activePanel.form;
51759             this.form.dialog = dlg;
51760             this.buttons[0].form = this.form;
51761             this.buttons[0].dialog = dlg;
51762             this.buttons[1].form = this.form;
51763             this.buttons[1].dialog = dlg;
51764            
51765            //this.resizeToLogo.defer(1000,this);
51766             // this is all related to resizing for logos..
51767             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
51768            //// if (!sz) {
51769              //   this.resizeToLogo.defer(1000,this);
51770              //   return;
51771            // }
51772             //var w = Ext.lib.Dom.getViewWidth() - 100;
51773             //var h = Ext.lib.Dom.getViewHeight() - 100;
51774             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
51775             //this.center();
51776             if (this.disabled) {
51777                 this.hide();
51778                 return;
51779             }
51780             
51781             if (this.user.id < 0) { // used for inital setup situations.
51782                 return;
51783             }
51784             
51785             if (this.intervalID) {
51786                 // remove the timer
51787                 window.clearInterval(this.intervalID);
51788                 this.intervalID = false;
51789             }
51790             
51791             
51792             if (Roo.get('loading')) {
51793                 Roo.get('loading').remove();
51794             }
51795             if (Roo.get('loading-mask')) {
51796                 Roo.get('loading-mask').hide();
51797             }
51798             
51799             //incomming._node = tnode;
51800             this.form.reset();
51801             //this.dialog.modal = !modal;
51802             //this.dialog.show();
51803             this.el.unmask(); 
51804             
51805             
51806             this.form.setValues({
51807                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
51808                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
51809             });
51810             
51811             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
51812             if (this.form.findField('username').getValue().length > 0 ){
51813                 this.form.findField('password').focus();
51814             } else {
51815                this.form.findField('username').focus();
51816             }
51817     
51818         }
51819     },
51820     items : [
51821          {
51822        
51823             xtype : 'ContentPanel',
51824             xns : Roo,
51825             region: 'center',
51826             fitToFrame : true,
51827             
51828             items : [
51829     
51830                 {
51831                
51832                     xtype : 'Form',
51833                     xns : Roo.form,
51834                     labelWidth: 100,
51835                     style : 'margin: 10px;',
51836                     
51837                     listeners : {
51838                         actionfailed : function(f, act) {
51839                             // form can return { errors: .... }
51840                                 
51841                             //act.result.errors // invalid form element list...
51842                             //act.result.errorMsg// invalid form element list...
51843                             
51844                             this.dialog.el.unmask();
51845                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
51846                                         "Login failed - communication error - try again.");
51847                                       
51848                         },
51849                         actioncomplete: function(re, act) {
51850                              
51851                             Roo.state.Manager.set(
51852                                 this.dialog.realm + '.username',  
51853                                     this.findField('username').getValue()
51854                             );
51855                             Roo.state.Manager.set(
51856                                 this.dialog.realm + '.lang',  
51857                                 this.findField('lang').getValue() 
51858                             );
51859                             
51860                             this.dialog.fillAuth(act.result.data);
51861                               
51862                             this.dialog.hide();
51863                             
51864                             if (Roo.get('loading-mask')) {
51865                                 Roo.get('loading-mask').show();
51866                             }
51867                             Roo.XComponent.build();
51868                             
51869                              
51870                             
51871                         }
51872                     },
51873                     items : [
51874                         {
51875                             xtype : 'TextField',
51876                             xns : Roo.form,
51877                             fieldLabel: "Email Address",
51878                             name: 'username',
51879                             width:200,
51880                             autoCreate : {tag: "input", type: "text", size: "20"}
51881                         },
51882                         {
51883                             xtype : 'TextField',
51884                             xns : Roo.form,
51885                             fieldLabel: "Password",
51886                             inputType: 'password',
51887                             name: 'password',
51888                             width:200,
51889                             autoCreate : {tag: "input", type: "text", size: "20"},
51890                             listeners : {
51891                                 specialkey : function(e,ev) {
51892                                     if (ev.keyCode == 13) {
51893                                         this.form.dialog.el.mask("Logging in");
51894                                         this.form.doAction('submit', {
51895                                             url: this.form.dialog.url,
51896                                             method: this.form.dialog.method
51897                                         });
51898                                     }
51899                                 }
51900                             }  
51901                         },
51902                         {
51903                             xtype : 'ComboBox',
51904                             xns : Roo.form,
51905                             fieldLabel: "Language",
51906                             name : 'langdisp',
51907                             store: {
51908                                 xtype : 'SimpleStore',
51909                                 fields: ['lang', 'ldisp'],
51910                                 data : [
51911                                     [ 'en', 'English' ],
51912                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
51913                                     [ 'zh_CN', '\u7C21\u4E2D' ]
51914                                 ]
51915                             },
51916                             
51917                             valueField : 'lang',
51918                             hiddenName:  'lang',
51919                             width: 200,
51920                             displayField:'ldisp',
51921                             typeAhead: false,
51922                             editable: false,
51923                             mode: 'local',
51924                             triggerAction: 'all',
51925                             emptyText:'Select a Language...',
51926                             selectOnFocus:true,
51927                             listeners : {
51928                                 select :  function(cb, rec, ix) {
51929                                     this.form.switchLang(rec.data.lang);
51930                                 }
51931                             }
51932                         
51933                         }
51934                     ]
51935                 }
51936                   
51937                 
51938             ]
51939         }
51940     ],
51941     buttons : [
51942         {
51943             xtype : 'Button',
51944             xns : 'Roo',
51945             text : "Forgot Password",
51946             listeners : {
51947                 click : function() {
51948                     //console.log(this);
51949                     var n = this.form.findField('username').getValue();
51950                     if (!n.length) {
51951                         Roo.MessageBox.alert("Error", "Fill in your email address");
51952                         return;
51953                     }
51954                     Roo.Ajax.request({
51955                         url: this.dialog.url,
51956                         params: {
51957                             passwordRequest: n
51958                         },
51959                         method: this.dialog.method,
51960                         success:  function(response, opts)  {  // check successfull...
51961                         
51962                             var res = this.dialog.processResponse(response);
51963                             if (!res.success) { // error!
51964                                Roo.MessageBox.alert("Error" ,
51965                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
51966                                return;
51967                             }
51968                             Roo.MessageBox.alert("Notice" ,
51969                                 "Please check you email for the Password Reset message");
51970                         },
51971                         failure : function() {
51972                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
51973                         }
51974                         
51975                     });
51976                 }
51977             }
51978         },
51979         {
51980             xtype : 'Button',
51981             xns : 'Roo',
51982             text : "Login",
51983             listeners : {
51984                 
51985                 click : function () {
51986                         
51987                     this.dialog.el.mask("Logging in");
51988                     this.form.doAction('submit', {
51989                             url: this.dialog.url,
51990                             method: this.dialog.method
51991                     });
51992                 }
51993             }
51994         }
51995     ]
51996   
51997   
51998 })
51999  
52000
52001
52002