sync
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{2,4})"};
1391     
1392     
1393     case "P":
1394         return {g:1,
1395                 c:[
1396                    "o = results[", currentGroup, "];\n",
1397                    "var sn = o.substring(0,1);\n",
1398                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1399                    "var mn = o.substring(4,6) % 60;\n",
1400                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1401                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1402             ].join(""),
1403             s:"([+\-]\\d{4})"};
1404     case "T":
1405         return {g:0,
1406             c:null,
1407             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1408     case "Z":
1409         return {g:1,
1410             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1411                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1412             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1413     default:
1414         return {g:0,
1415             c:null,
1416             s:String.escape(character)};
1417     }
1418 };
1419
1420 /**
1421  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1422  * @return {String} The abbreviated timezone name (e.g. 'CST')
1423  */
1424 Date.prototype.getTimezone = function() {
1425     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1426 };
1427
1428 /**
1429  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1430  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1431  */
1432 Date.prototype.getGMTOffset = function() {
1433     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1434         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1435         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1436 };
1437
1438 /**
1439  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1440  * @return {String} 2-characters representing hours and 2-characters representing minutes
1441  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1442  */
1443 Date.prototype.getGMTColonOffset = function() {
1444         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1445                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1446                 + ":"
1447                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1448 }
1449
1450 /**
1451  * Get the numeric day number of the year, adjusted for leap year.
1452  * @return {Number} 0 through 364 (365 in leap years)
1453  */
1454 Date.prototype.getDayOfYear = function() {
1455     var num = 0;
1456     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1457     for (var i = 0; i < this.getMonth(); ++i) {
1458         num += Date.daysInMonth[i];
1459     }
1460     return num + this.getDate() - 1;
1461 };
1462
1463 /**
1464  * Get the string representation of the numeric week number of the year
1465  * (equivalent to the format specifier 'W').
1466  * @return {String} '00' through '52'
1467  */
1468 Date.prototype.getWeekOfYear = function() {
1469     // Skip to Thursday of this week
1470     var now = this.getDayOfYear() + (4 - this.getDay());
1471     // Find the first Thursday of the year
1472     var jan1 = new Date(this.getFullYear(), 0, 1);
1473     var then = (7 - jan1.getDay() + 4);
1474     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1475 };
1476
1477 /**
1478  * Whether or not the current date is in a leap year.
1479  * @return {Boolean} True if the current date is in a leap year, else false
1480  */
1481 Date.prototype.isLeapYear = function() {
1482     var year = this.getFullYear();
1483     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1484 };
1485
1486 /**
1487  * Get the first day of the current month, adjusted for leap year.  The returned value
1488  * is the numeric day index within the week (0-6) which can be used in conjunction with
1489  * the {@link #monthNames} array to retrieve the textual day name.
1490  * Example:
1491  *<pre><code>
1492 var dt = new Date('1/10/2007');
1493 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1494 </code></pre>
1495  * @return {Number} The day number (0-6)
1496  */
1497 Date.prototype.getFirstDayOfMonth = function() {
1498     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1499     return (day < 0) ? (day + 7) : day;
1500 };
1501
1502 /**
1503  * Get the last day of the current month, adjusted for leap year.  The returned value
1504  * is the numeric day index within the week (0-6) which can be used in conjunction with
1505  * the {@link #monthNames} array to retrieve the textual day name.
1506  * Example:
1507  *<pre><code>
1508 var dt = new Date('1/10/2007');
1509 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1510 </code></pre>
1511  * @return {Number} The day number (0-6)
1512  */
1513 Date.prototype.getLastDayOfMonth = function() {
1514     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1515     return (day < 0) ? (day + 7) : day;
1516 };
1517
1518
1519 /**
1520  * Get the first date of this date's month
1521  * @return {Date}
1522  */
1523 Date.prototype.getFirstDateOfMonth = function() {
1524     return new Date(this.getFullYear(), this.getMonth(), 1);
1525 };
1526
1527 /**
1528  * Get the last date of this date's month
1529  * @return {Date}
1530  */
1531 Date.prototype.getLastDateOfMonth = function() {
1532     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1533 };
1534 /**
1535  * Get the number of days in the current month, adjusted for leap year.
1536  * @return {Number} The number of days in the month
1537  */
1538 Date.prototype.getDaysInMonth = function() {
1539     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1540     return Date.daysInMonth[this.getMonth()];
1541 };
1542
1543 /**
1544  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1545  * @return {String} 'st, 'nd', 'rd' or 'th'
1546  */
1547 Date.prototype.getSuffix = function() {
1548     switch (this.getDate()) {
1549         case 1:
1550         case 21:
1551         case 31:
1552             return "st";
1553         case 2:
1554         case 22:
1555             return "nd";
1556         case 3:
1557         case 23:
1558             return "rd";
1559         default:
1560             return "th";
1561     }
1562 };
1563
1564 // private
1565 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1566
1567 /**
1568  * An array of textual month names.
1569  * Override these values for international dates, for example...
1570  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1571  * @type Array
1572  * @static
1573  */
1574 Date.monthNames =
1575    ["January",
1576     "February",
1577     "March",
1578     "April",
1579     "May",
1580     "June",
1581     "July",
1582     "August",
1583     "September",
1584     "October",
1585     "November",
1586     "December"];
1587
1588 /**
1589  * An array of textual day names.
1590  * Override these values for international dates, for example...
1591  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1592  * @type Array
1593  * @static
1594  */
1595 Date.dayNames =
1596    ["Sunday",
1597     "Monday",
1598     "Tuesday",
1599     "Wednesday",
1600     "Thursday",
1601     "Friday",
1602     "Saturday"];
1603
1604 // private
1605 Date.y2kYear = 50;
1606 // private
1607 Date.monthNumbers = {
1608     Jan:0,
1609     Feb:1,
1610     Mar:2,
1611     Apr:3,
1612     May:4,
1613     Jun:5,
1614     Jul:6,
1615     Aug:7,
1616     Sep:8,
1617     Oct:9,
1618     Nov:10,
1619     Dec:11};
1620
1621 /**
1622  * Creates and returns a new Date instance with the exact same date value as the called instance.
1623  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1624  * variable will also be changed.  When the intention is to create a new variable that will not
1625  * modify the original instance, you should create a clone.
1626  *
1627  * Example of correctly cloning a date:
1628  * <pre><code>
1629 //wrong way:
1630 var orig = new Date('10/1/2006');
1631 var copy = orig;
1632 copy.setDate(5);
1633 document.write(orig);  //returns 'Thu Oct 05 2006'!
1634
1635 //correct way:
1636 var orig = new Date('10/1/2006');
1637 var copy = orig.clone();
1638 copy.setDate(5);
1639 document.write(orig);  //returns 'Thu Oct 01 2006'
1640 </code></pre>
1641  * @return {Date} The new Date instance
1642  */
1643 Date.prototype.clone = function() {
1644         return new Date(this.getTime());
1645 };
1646
1647 /**
1648  * Clears any time information from this date
1649  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1650  @return {Date} this or the clone
1651  */
1652 Date.prototype.clearTime = function(clone){
1653     if(clone){
1654         return this.clone().clearTime();
1655     }
1656     this.setHours(0);
1657     this.setMinutes(0);
1658     this.setSeconds(0);
1659     this.setMilliseconds(0);
1660     return this;
1661 };
1662
1663 // private
1664 // safari setMonth is broken
1665 if(Roo.isSafari){
1666     Date.brokenSetMonth = Date.prototype.setMonth;
1667         Date.prototype.setMonth = function(num){
1668                 if(num <= -1){
1669                         var n = Math.ceil(-num);
1670                         var back_year = Math.ceil(n/12);
1671                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1672                         this.setFullYear(this.getFullYear() - back_year);
1673                         return Date.brokenSetMonth.call(this, month);
1674                 } else {
1675                         return Date.brokenSetMonth.apply(this, arguments);
1676                 }
1677         };
1678 }
1679
1680 /** Date interval constant 
1681 * @static 
1682 * @type String */
1683 Date.MILLI = "ms";
1684 /** Date interval constant 
1685 * @static 
1686 * @type String */
1687 Date.SECOND = "s";
1688 /** Date interval constant 
1689 * @static 
1690 * @type String */
1691 Date.MINUTE = "mi";
1692 /** Date interval constant 
1693 * @static 
1694 * @type String */
1695 Date.HOUR = "h";
1696 /** Date interval constant 
1697 * @static 
1698 * @type String */
1699 Date.DAY = "d";
1700 /** Date interval constant 
1701 * @static 
1702 * @type String */
1703 Date.MONTH = "mo";
1704 /** Date interval constant 
1705 * @static 
1706 * @type String */
1707 Date.YEAR = "y";
1708
1709 /**
1710  * Provides a convenient method of performing basic date arithmetic.  This method
1711  * does not modify the Date instance being called - it creates and returns
1712  * a new Date instance containing the resulting date value.
1713  *
1714  * Examples:
1715  * <pre><code>
1716 //Basic usage:
1717 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1718 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1719
1720 //Negative values will subtract correctly:
1721 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1722 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1723
1724 //You can even chain several calls together in one line!
1725 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1726 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1727  </code></pre>
1728  *
1729  * @param {String} interval   A valid date interval enum value
1730  * @param {Number} value      The amount to add to the current date
1731  * @return {Date} The new Date instance
1732  */
1733 Date.prototype.add = function(interval, value){
1734   var d = this.clone();
1735   if (!interval || value === 0) return d;
1736   switch(interval.toLowerCase()){
1737     case Date.MILLI:
1738       d.setMilliseconds(this.getMilliseconds() + value);
1739       break;
1740     case Date.SECOND:
1741       d.setSeconds(this.getSeconds() + value);
1742       break;
1743     case Date.MINUTE:
1744       d.setMinutes(this.getMinutes() + value);
1745       break;
1746     case Date.HOUR:
1747       d.setHours(this.getHours() + value);
1748       break;
1749     case Date.DAY:
1750       d.setDate(this.getDate() + value);
1751       break;
1752     case Date.MONTH:
1753       var day = this.getDate();
1754       if(day > 28){
1755           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1756       }
1757       d.setDate(day);
1758       d.setMonth(this.getMonth() + value);
1759       break;
1760     case Date.YEAR:
1761       d.setFullYear(this.getFullYear() + value);
1762       break;
1763   }
1764   return d;
1765 };
1766 /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776
1777 /**
1778  * @class Roo.lib.Dom
1779  * @static
1780  * 
1781  * Dom utils (from YIU afaik)
1782  * 
1783  **/
1784 Roo.lib.Dom = {
1785     /**
1786      * Get the view width
1787      * @param {Boolean} full True will get the full document, otherwise it's the view width
1788      * @return {Number} The width
1789      */
1790      
1791     getViewWidth : function(full) {
1792         return full ? this.getDocumentWidth() : this.getViewportWidth();
1793     },
1794     /**
1795      * Get the view height
1796      * @param {Boolean} full True will get the full document, otherwise it's the view height
1797      * @return {Number} The height
1798      */
1799     getViewHeight : function(full) {
1800         return full ? this.getDocumentHeight() : this.getViewportHeight();
1801     },
1802
1803     getDocumentHeight: function() {
1804         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1805         return Math.max(scrollHeight, this.getViewportHeight());
1806     },
1807
1808     getDocumentWidth: function() {
1809         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1810         return Math.max(scrollWidth, this.getViewportWidth());
1811     },
1812
1813     getViewportHeight: function() {
1814         var height = self.innerHeight;
1815         var mode = document.compatMode;
1816
1817         if ((mode || Roo.isIE) && !Roo.isOpera) {
1818             height = (mode == "CSS1Compat") ?
1819                      document.documentElement.clientHeight :
1820                      document.body.clientHeight;
1821         }
1822
1823         return height;
1824     },
1825
1826     getViewportWidth: function() {
1827         var width = self.innerWidth;
1828         var mode = document.compatMode;
1829
1830         if (mode || Roo.isIE) {
1831             width = (mode == "CSS1Compat") ?
1832                     document.documentElement.clientWidth :
1833                     document.body.clientWidth;
1834         }
1835         return width;
1836     },
1837
1838     isAncestor : function(p, c) {
1839         p = Roo.getDom(p);
1840         c = Roo.getDom(c);
1841         if (!p || !c) {
1842             return false;
1843         }
1844
1845         if (p.contains && !Roo.isSafari) {
1846             return p.contains(c);
1847         } else if (p.compareDocumentPosition) {
1848             return !!(p.compareDocumentPosition(c) & 16);
1849         } else {
1850             var parent = c.parentNode;
1851             while (parent) {
1852                 if (parent == p) {
1853                     return true;
1854                 }
1855                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1856                     return false;
1857                 }
1858                 parent = parent.parentNode;
1859             }
1860             return false;
1861         }
1862     },
1863
1864     getRegion : function(el) {
1865         return Roo.lib.Region.getRegion(el);
1866     },
1867
1868     getY : function(el) {
1869         return this.getXY(el)[1];
1870     },
1871
1872     getX : function(el) {
1873         return this.getXY(el)[0];
1874     },
1875
1876     getXY : function(el) {
1877         var p, pe, b, scroll, bd = document.body;
1878         el = Roo.getDom(el);
1879         var fly = Roo.lib.AnimBase.fly;
1880         if (el.getBoundingClientRect) {
1881             b = el.getBoundingClientRect();
1882             scroll = fly(document).getScroll();
1883             return [b.left + scroll.left, b.top + scroll.top];
1884         }
1885         var x = 0, y = 0;
1886
1887         p = el;
1888
1889         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1890
1891         while (p) {
1892
1893             x += p.offsetLeft;
1894             y += p.offsetTop;
1895
1896             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1897                 hasAbsolute = true;
1898             }
1899
1900             if (Roo.isGecko) {
1901                 pe = fly(p);
1902
1903                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1904                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1905
1906
1907                 x += bl;
1908                 y += bt;
1909
1910
1911                 if (p != el && pe.getStyle('overflow') != 'visible') {
1912                     x += bl;
1913                     y += bt;
1914                 }
1915             }
1916             p = p.offsetParent;
1917         }
1918
1919         if (Roo.isSafari && hasAbsolute) {
1920             x -= bd.offsetLeft;
1921             y -= bd.offsetTop;
1922         }
1923
1924         if (Roo.isGecko && !hasAbsolute) {
1925             var dbd = fly(bd);
1926             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1927             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1928         }
1929
1930         p = el.parentNode;
1931         while (p && p != bd) {
1932             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1933                 x -= p.scrollLeft;
1934                 y -= p.scrollTop;
1935             }
1936             p = p.parentNode;
1937         }
1938         return [x, y];
1939     },
1940  
1941   
1942
1943
1944     setXY : function(el, xy) {
1945         el = Roo.fly(el, '_setXY');
1946         el.position();
1947         var pts = el.translatePoints(xy);
1948         if (xy[0] !== false) {
1949             el.dom.style.left = pts.left + "px";
1950         }
1951         if (xy[1] !== false) {
1952             el.dom.style.top = pts.top + "px";
1953         }
1954     },
1955
1956     setX : function(el, x) {
1957         this.setXY(el, [x, false]);
1958     },
1959
1960     setY : function(el, y) {
1961         this.setXY(el, [false, y]);
1962     }
1963 };
1964 /*
1965  * Portions of this file are based on pieces of Yahoo User Interface Library
1966  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1967  * YUI licensed under the BSD License:
1968  * http://developer.yahoo.net/yui/license.txt
1969  * <script type="text/javascript">
1970  *
1971  */
1972
1973 Roo.lib.Event = function() {
1974     var loadComplete = false;
1975     var listeners = [];
1976     var unloadListeners = [];
1977     var retryCount = 0;
1978     var onAvailStack = [];
1979     var counter = 0;
1980     var lastError = null;
1981
1982     return {
1983         POLL_RETRYS: 200,
1984         POLL_INTERVAL: 20,
1985         EL: 0,
1986         TYPE: 1,
1987         FN: 2,
1988         WFN: 3,
1989         OBJ: 3,
1990         ADJ_SCOPE: 4,
1991         _interval: null,
1992
1993         startInterval: function() {
1994             if (!this._interval) {
1995                 var self = this;
1996                 var callback = function() {
1997                     self._tryPreloadAttach();
1998                 };
1999                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2000
2001             }
2002         },
2003
2004         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2005             onAvailStack.push({ id:         p_id,
2006                 fn:         p_fn,
2007                 obj:        p_obj,
2008                 override:   p_override,
2009                 checkReady: false    });
2010
2011             retryCount = this.POLL_RETRYS;
2012             this.startInterval();
2013         },
2014
2015
2016         addListener: function(el, eventName, fn) {
2017             el = Roo.getDom(el);
2018             if (!el || !fn) {
2019                 return false;
2020             }
2021
2022             if ("unload" == eventName) {
2023                 unloadListeners[unloadListeners.length] =
2024                 [el, eventName, fn];
2025                 return true;
2026             }
2027
2028             var wrappedFn = function(e) {
2029                 return fn(Roo.lib.Event.getEvent(e));
2030             };
2031
2032             var li = [el, eventName, fn, wrappedFn];
2033
2034             var index = listeners.length;
2035             listeners[index] = li;
2036
2037             this.doAdd(el, eventName, wrappedFn, false);
2038             return true;
2039
2040         },
2041
2042
2043         removeListener: function(el, eventName, fn) {
2044             var i, len;
2045
2046             el = Roo.getDom(el);
2047
2048             if(!fn) {
2049                 return this.purgeElement(el, false, eventName);
2050             }
2051
2052
2053             if ("unload" == eventName) {
2054
2055                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2056                     var li = unloadListeners[i];
2057                     if (li &&
2058                         li[0] == el &&
2059                         li[1] == eventName &&
2060                         li[2] == fn) {
2061                         unloadListeners.splice(i, 1);
2062                         return true;
2063                     }
2064                 }
2065
2066                 return false;
2067             }
2068
2069             var cacheItem = null;
2070
2071
2072             var index = arguments[3];
2073
2074             if ("undefined" == typeof index) {
2075                 index = this._getCacheIndex(el, eventName, fn);
2076             }
2077
2078             if (index >= 0) {
2079                 cacheItem = listeners[index];
2080             }
2081
2082             if (!el || !cacheItem) {
2083                 return false;
2084             }
2085
2086             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2087
2088             delete listeners[index][this.WFN];
2089             delete listeners[index][this.FN];
2090             listeners.splice(index, 1);
2091
2092             return true;
2093
2094         },
2095
2096
2097         getTarget: function(ev, resolveTextNode) {
2098             ev = ev.browserEvent || ev;
2099             var t = ev.target || ev.srcElement;
2100             return this.resolveTextNode(t);
2101         },
2102
2103
2104         resolveTextNode: function(node) {
2105             if (Roo.isSafari && node && 3 == node.nodeType) {
2106                 return node.parentNode;
2107             } else {
2108                 return node;
2109             }
2110         },
2111
2112
2113         getPageX: function(ev) {
2114             ev = ev.browserEvent || ev;
2115             var x = ev.pageX;
2116             if (!x && 0 !== x) {
2117                 x = ev.clientX || 0;
2118
2119                 if (Roo.isIE) {
2120                     x += this.getScroll()[1];
2121                 }
2122             }
2123
2124             return x;
2125         },
2126
2127
2128         getPageY: function(ev) {
2129             ev = ev.browserEvent || ev;
2130             var y = ev.pageY;
2131             if (!y && 0 !== y) {
2132                 y = ev.clientY || 0;
2133
2134                 if (Roo.isIE) {
2135                     y += this.getScroll()[0];
2136                 }
2137             }
2138
2139
2140             return y;
2141         },
2142
2143
2144         getXY: function(ev) {
2145             ev = ev.browserEvent || ev;
2146             return [this.getPageX(ev), this.getPageY(ev)];
2147         },
2148
2149
2150         getRelatedTarget: function(ev) {
2151             ev = ev.browserEvent || ev;
2152             var t = ev.relatedTarget;
2153             if (!t) {
2154                 if (ev.type == "mouseout") {
2155                     t = ev.toElement;
2156                 } else if (ev.type == "mouseover") {
2157                     t = ev.fromElement;
2158                 }
2159             }
2160
2161             return this.resolveTextNode(t);
2162         },
2163
2164
2165         getTime: function(ev) {
2166             ev = ev.browserEvent || ev;
2167             if (!ev.time) {
2168                 var t = new Date().getTime();
2169                 try {
2170                     ev.time = t;
2171                 } catch(ex) {
2172                     this.lastError = ex;
2173                     return t;
2174                 }
2175             }
2176
2177             return ev.time;
2178         },
2179
2180
2181         stopEvent: function(ev) {
2182             this.stopPropagation(ev);
2183             this.preventDefault(ev);
2184         },
2185
2186
2187         stopPropagation: function(ev) {
2188             ev = ev.browserEvent || ev;
2189             if (ev.stopPropagation) {
2190                 ev.stopPropagation();
2191             } else {
2192                 ev.cancelBubble = true;
2193             }
2194         },
2195
2196
2197         preventDefault: function(ev) {
2198             ev = ev.browserEvent || ev;
2199             if(ev.preventDefault) {
2200                 ev.preventDefault();
2201             } else {
2202                 ev.returnValue = false;
2203             }
2204         },
2205
2206
2207         getEvent: function(e) {
2208             var ev = e || window.event;
2209             if (!ev) {
2210                 var c = this.getEvent.caller;
2211                 while (c) {
2212                     ev = c.arguments[0];
2213                     if (ev && Event == ev.constructor) {
2214                         break;
2215                     }
2216                     c = c.caller;
2217                 }
2218             }
2219             return ev;
2220         },
2221
2222
2223         getCharCode: function(ev) {
2224             ev = ev.browserEvent || ev;
2225             return ev.charCode || ev.keyCode || 0;
2226         },
2227
2228
2229         _getCacheIndex: function(el, eventName, fn) {
2230             for (var i = 0,len = listeners.length; i < len; ++i) {
2231                 var li = listeners[i];
2232                 if (li &&
2233                     li[this.FN] == fn &&
2234                     li[this.EL] == el &&
2235                     li[this.TYPE] == eventName) {
2236                     return i;
2237                 }
2238             }
2239
2240             return -1;
2241         },
2242
2243
2244         elCache: {},
2245
2246
2247         getEl: function(id) {
2248             return document.getElementById(id);
2249         },
2250
2251
2252         clearCache: function() {
2253         },
2254
2255
2256         _load: function(e) {
2257             loadComplete = true;
2258             var EU = Roo.lib.Event;
2259
2260
2261             if (Roo.isIE) {
2262                 EU.doRemove(window, "load", EU._load);
2263             }
2264         },
2265
2266
2267         _tryPreloadAttach: function() {
2268
2269             if (this.locked) {
2270                 return false;
2271             }
2272
2273             this.locked = true;
2274
2275
2276             var tryAgain = !loadComplete;
2277             if (!tryAgain) {
2278                 tryAgain = (retryCount > 0);
2279             }
2280
2281
2282             var notAvail = [];
2283             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2284                 var item = onAvailStack[i];
2285                 if (item) {
2286                     var el = this.getEl(item.id);
2287
2288                     if (el) {
2289                         if (!item.checkReady ||
2290                             loadComplete ||
2291                             el.nextSibling ||
2292                             (document && document.body)) {
2293
2294                             var scope = el;
2295                             if (item.override) {
2296                                 if (item.override === true) {
2297                                     scope = item.obj;
2298                                 } else {
2299                                     scope = item.override;
2300                                 }
2301                             }
2302                             item.fn.call(scope, item.obj);
2303                             onAvailStack[i] = null;
2304                         }
2305                     } else {
2306                         notAvail.push(item);
2307                     }
2308                 }
2309             }
2310
2311             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2312
2313             if (tryAgain) {
2314
2315                 this.startInterval();
2316             } else {
2317                 clearInterval(this._interval);
2318                 this._interval = null;
2319             }
2320
2321             this.locked = false;
2322
2323             return true;
2324
2325         },
2326
2327
2328         purgeElement: function(el, recurse, eventName) {
2329             var elListeners = this.getListeners(el, eventName);
2330             if (elListeners) {
2331                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2332                     var l = elListeners[i];
2333                     this.removeListener(el, l.type, l.fn);
2334                 }
2335             }
2336
2337             if (recurse && el && el.childNodes) {
2338                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2339                     this.purgeElement(el.childNodes[i], recurse, eventName);
2340                 }
2341             }
2342         },
2343
2344
2345         getListeners: function(el, eventName) {
2346             var results = [], searchLists;
2347             if (!eventName) {
2348                 searchLists = [listeners, unloadListeners];
2349             } else if (eventName == "unload") {
2350                 searchLists = [unloadListeners];
2351             } else {
2352                 searchLists = [listeners];
2353             }
2354
2355             for (var j = 0; j < searchLists.length; ++j) {
2356                 var searchList = searchLists[j];
2357                 if (searchList && searchList.length > 0) {
2358                     for (var i = 0,len = searchList.length; i < len; ++i) {
2359                         var l = searchList[i];
2360                         if (l && l[this.EL] === el &&
2361                             (!eventName || eventName === l[this.TYPE])) {
2362                             results.push({
2363                                 type:   l[this.TYPE],
2364                                 fn:     l[this.FN],
2365                                 obj:    l[this.OBJ],
2366                                 adjust: l[this.ADJ_SCOPE],
2367                                 index:  i
2368                             });
2369                         }
2370                     }
2371                 }
2372             }
2373
2374             return (results.length) ? results : null;
2375         },
2376
2377
2378         _unload: function(e) {
2379
2380             var EU = Roo.lib.Event, i, j, l, len, index;
2381
2382             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2383                 l = unloadListeners[i];
2384                 if (l) {
2385                     var scope = window;
2386                     if (l[EU.ADJ_SCOPE]) {
2387                         if (l[EU.ADJ_SCOPE] === true) {
2388                             scope = l[EU.OBJ];
2389                         } else {
2390                             scope = l[EU.ADJ_SCOPE];
2391                         }
2392                     }
2393                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2394                     unloadListeners[i] = null;
2395                     l = null;
2396                     scope = null;
2397                 }
2398             }
2399
2400             unloadListeners = null;
2401
2402             if (listeners && listeners.length > 0) {
2403                 j = listeners.length;
2404                 while (j) {
2405                     index = j - 1;
2406                     l = listeners[index];
2407                     if (l) {
2408                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2409                                 l[EU.FN], index);
2410                     }
2411                     j = j - 1;
2412                 }
2413                 l = null;
2414
2415                 EU.clearCache();
2416             }
2417
2418             EU.doRemove(window, "unload", EU._unload);
2419
2420         },
2421
2422
2423         getScroll: function() {
2424             var dd = document.documentElement, db = document.body;
2425             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2426                 return [dd.scrollTop, dd.scrollLeft];
2427             } else if (db) {
2428                 return [db.scrollTop, db.scrollLeft];
2429             } else {
2430                 return [0, 0];
2431             }
2432         },
2433
2434
2435         doAdd: function () {
2436             if (window.addEventListener) {
2437                 return function(el, eventName, fn, capture) {
2438                     el.addEventListener(eventName, fn, (capture));
2439                 };
2440             } else if (window.attachEvent) {
2441                 return function(el, eventName, fn, capture) {
2442                     el.attachEvent("on" + eventName, fn);
2443                 };
2444             } else {
2445                 return function() {
2446                 };
2447             }
2448         }(),
2449
2450
2451         doRemove: function() {
2452             if (window.removeEventListener) {
2453                 return function (el, eventName, fn, capture) {
2454                     el.removeEventListener(eventName, fn, (capture));
2455                 };
2456             } else if (window.detachEvent) {
2457                 return function (el, eventName, fn) {
2458                     el.detachEvent("on" + eventName, fn);
2459                 };
2460             } else {
2461                 return function() {
2462                 };
2463             }
2464         }()
2465     };
2466     
2467 }();
2468 (function() {     
2469    
2470     var E = Roo.lib.Event;
2471     E.on = E.addListener;
2472     E.un = E.removeListener;
2473
2474     if (document && document.body) {
2475         E._load();
2476     } else {
2477         E.doAdd(window, "load", E._load);
2478     }
2479     E.doAdd(window, "unload", E._unload);
2480     E._tryPreloadAttach();
2481 })();
2482
2483 /*
2484  * Portions of this file are based on pieces of Yahoo User Interface Library
2485  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2486  * YUI licensed under the BSD License:
2487  * http://developer.yahoo.net/yui/license.txt
2488  * <script type="text/javascript">
2489  *
2490  */
2491
2492 (function() {
2493     /**
2494      * @class Roo.lib.Ajax
2495      *
2496      */
2497     Roo.lib.Ajax = {
2498         /**
2499          * @static 
2500          */
2501         request : function(method, uri, cb, data, options) {
2502             if(options){
2503                 var hs = options.headers;
2504                 if(hs){
2505                     for(var h in hs){
2506                         if(hs.hasOwnProperty(h)){
2507                             this.initHeader(h, hs[h], false);
2508                         }
2509                     }
2510                 }
2511                 if(options.xmlData){
2512                     this.initHeader('Content-Type', 'text/xml', false);
2513                     method = 'POST';
2514                     data = options.xmlData;
2515                 }
2516             }
2517
2518             return this.asyncRequest(method, uri, cb, data);
2519         },
2520
2521         serializeForm : function(form) {
2522             if(typeof form == 'string') {
2523                 form = (document.getElementById(form) || document.forms[form]);
2524             }
2525
2526             var el, name, val, disabled, data = '', hasSubmit = false;
2527             for (var i = 0; i < form.elements.length; i++) {
2528                 el = form.elements[i];
2529                 disabled = form.elements[i].disabled;
2530                 name = form.elements[i].name;
2531                 val = form.elements[i].value;
2532
2533                 if (!disabled && name){
2534                     switch (el.type)
2535                             {
2536                         case 'select-one':
2537                         case 'select-multiple':
2538                             for (var j = 0; j < el.options.length; j++) {
2539                                 if (el.options[j].selected) {
2540                                     if (Roo.isIE) {
2541                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2542                                     }
2543                                     else {
2544                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2545                                     }
2546                                 }
2547                             }
2548                             break;
2549                         case 'radio':
2550                         case 'checkbox':
2551                             if (el.checked) {
2552                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             }
2554                             break;
2555                         case 'file':
2556
2557                         case undefined:
2558
2559                         case 'reset':
2560
2561                         case 'button':
2562
2563                             break;
2564                         case 'submit':
2565                             if(hasSubmit == false) {
2566                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2567                                 hasSubmit = true;
2568                             }
2569                             break;
2570                         default:
2571                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2572                             break;
2573                     }
2574                 }
2575             }
2576             data = data.substr(0, data.length - 1);
2577             return data;
2578         },
2579
2580         headers:{},
2581
2582         hasHeaders:false,
2583
2584         useDefaultHeader:true,
2585
2586         defaultPostHeader:'application/x-www-form-urlencoded',
2587
2588         useDefaultXhrHeader:true,
2589
2590         defaultXhrHeader:'XMLHttpRequest',
2591
2592         hasDefaultHeaders:true,
2593
2594         defaultHeaders:{},
2595
2596         poll:{},
2597
2598         timeout:{},
2599
2600         pollInterval:50,
2601
2602         transactionId:0,
2603
2604         setProgId:function(id)
2605         {
2606             this.activeX.unshift(id);
2607         },
2608
2609         setDefaultPostHeader:function(b)
2610         {
2611             this.useDefaultHeader = b;
2612         },
2613
2614         setDefaultXhrHeader:function(b)
2615         {
2616             this.useDefaultXhrHeader = b;
2617         },
2618
2619         setPollingInterval:function(i)
2620         {
2621             if (typeof i == 'number' && isFinite(i)) {
2622                 this.pollInterval = i;
2623             }
2624         },
2625
2626         createXhrObject:function(transactionId)
2627         {
2628             var obj,http;
2629             try
2630             {
2631
2632                 http = new XMLHttpRequest();
2633
2634                 obj = { conn:http, tId:transactionId };
2635             }
2636             catch(e)
2637             {
2638                 for (var i = 0; i < this.activeX.length; ++i) {
2639                     try
2640                     {
2641
2642                         http = new ActiveXObject(this.activeX[i]);
2643
2644                         obj = { conn:http, tId:transactionId };
2645                         break;
2646                     }
2647                     catch(e) {
2648                     }
2649                 }
2650             }
2651             finally
2652             {
2653                 return obj;
2654             }
2655         },
2656
2657         getConnectionObject:function()
2658         {
2659             var o;
2660             var tId = this.transactionId;
2661
2662             try
2663             {
2664                 o = this.createXhrObject(tId);
2665                 if (o) {
2666                     this.transactionId++;
2667                 }
2668             }
2669             catch(e) {
2670             }
2671             finally
2672             {
2673                 return o;
2674             }
2675         },
2676
2677         asyncRequest:function(method, uri, callback, postData)
2678         {
2679             var o = this.getConnectionObject();
2680
2681             if (!o) {
2682                 return null;
2683             }
2684             else {
2685                 o.conn.open(method, uri, true);
2686
2687                 if (this.useDefaultXhrHeader) {
2688                     if (!this.defaultHeaders['X-Requested-With']) {
2689                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2690                     }
2691                 }
2692
2693                 if(postData && this.useDefaultHeader){
2694                     this.initHeader('Content-Type', this.defaultPostHeader);
2695                 }
2696
2697                  if (this.hasDefaultHeaders || this.hasHeaders) {
2698                     this.setHeader(o);
2699                 }
2700
2701                 this.handleReadyState(o, callback);
2702                 o.conn.send(postData || null);
2703
2704                 return o;
2705             }
2706         },
2707
2708         handleReadyState:function(o, callback)
2709         {
2710             var oConn = this;
2711
2712             if (callback && callback.timeout) {
2713                 this.timeout[o.tId] = window.setTimeout(function() {
2714                     oConn.abort(o, callback, true);
2715                 }, callback.timeout);
2716             }
2717
2718             this.poll[o.tId] = window.setInterval(
2719                     function() {
2720                         if (o.conn && o.conn.readyState == 4) {
2721                             window.clearInterval(oConn.poll[o.tId]);
2722                             delete oConn.poll[o.tId];
2723
2724                             if(callback && callback.timeout) {
2725                                 window.clearTimeout(oConn.timeout[o.tId]);
2726                                 delete oConn.timeout[o.tId];
2727                             }
2728
2729                             oConn.handleTransactionResponse(o, callback);
2730                         }
2731                     }
2732                     , this.pollInterval);
2733         },
2734
2735         handleTransactionResponse:function(o, callback, isAbort)
2736         {
2737
2738             if (!callback) {
2739                 this.releaseObject(o);
2740                 return;
2741             }
2742
2743             var httpStatus, responseObject;
2744
2745             try
2746             {
2747                 if (o.conn.status !== undefined && o.conn.status != 0) {
2748                     httpStatus = o.conn.status;
2749                 }
2750                 else {
2751                     httpStatus = 13030;
2752                 }
2753             }
2754             catch(e) {
2755
2756
2757                 httpStatus = 13030;
2758             }
2759
2760             if (httpStatus >= 200 && httpStatus < 300) {
2761                 responseObject = this.createResponseObject(o, callback.argument);
2762                 if (callback.success) {
2763                     if (!callback.scope) {
2764                         callback.success(responseObject);
2765                     }
2766                     else {
2767
2768
2769                         callback.success.apply(callback.scope, [responseObject]);
2770                     }
2771                 }
2772             }
2773             else {
2774                 switch (httpStatus) {
2775
2776                     case 12002:
2777                     case 12029:
2778                     case 12030:
2779                     case 12031:
2780                     case 12152:
2781                     case 13030:
2782                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2783                         if (callback.failure) {
2784                             if (!callback.scope) {
2785                                 callback.failure(responseObject);
2786                             }
2787                             else {
2788                                 callback.failure.apply(callback.scope, [responseObject]);
2789                             }
2790                         }
2791                         break;
2792                     default:
2793                         responseObject = this.createResponseObject(o, callback.argument);
2794                         if (callback.failure) {
2795                             if (!callback.scope) {
2796                                 callback.failure(responseObject);
2797                             }
2798                             else {
2799                                 callback.failure.apply(callback.scope, [responseObject]);
2800                             }
2801                         }
2802                 }
2803             }
2804
2805             this.releaseObject(o);
2806             responseObject = null;
2807         },
2808
2809         createResponseObject:function(o, callbackArg)
2810         {
2811             var obj = {};
2812             var headerObj = {};
2813
2814             try
2815             {
2816                 var headerStr = o.conn.getAllResponseHeaders();
2817                 var header = headerStr.split('\n');
2818                 for (var i = 0; i < header.length; i++) {
2819                     var delimitPos = header[i].indexOf(':');
2820                     if (delimitPos != -1) {
2821                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2822                     }
2823                 }
2824             }
2825             catch(e) {
2826             }
2827
2828             obj.tId = o.tId;
2829             obj.status = o.conn.status;
2830             obj.statusText = o.conn.statusText;
2831             obj.getResponseHeader = headerObj;
2832             obj.getAllResponseHeaders = headerStr;
2833             obj.responseText = o.conn.responseText;
2834             obj.responseXML = o.conn.responseXML;
2835
2836             if (typeof callbackArg !== undefined) {
2837                 obj.argument = callbackArg;
2838             }
2839
2840             return obj;
2841         },
2842
2843         createExceptionObject:function(tId, callbackArg, isAbort)
2844         {
2845             var COMM_CODE = 0;
2846             var COMM_ERROR = 'communication failure';
2847             var ABORT_CODE = -1;
2848             var ABORT_ERROR = 'transaction aborted';
2849
2850             var obj = {};
2851
2852             obj.tId = tId;
2853             if (isAbort) {
2854                 obj.status = ABORT_CODE;
2855                 obj.statusText = ABORT_ERROR;
2856             }
2857             else {
2858                 obj.status = COMM_CODE;
2859                 obj.statusText = COMM_ERROR;
2860             }
2861
2862             if (callbackArg) {
2863                 obj.argument = callbackArg;
2864             }
2865
2866             return obj;
2867         },
2868
2869         initHeader:function(label, value, isDefault)
2870         {
2871             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2872
2873             if (headerObj[label] === undefined) {
2874                 headerObj[label] = value;
2875             }
2876             else {
2877
2878
2879                 headerObj[label] = value + "," + headerObj[label];
2880             }
2881
2882             if (isDefault) {
2883                 this.hasDefaultHeaders = true;
2884             }
2885             else {
2886                 this.hasHeaders = true;
2887             }
2888         },
2889
2890
2891         setHeader:function(o)
2892         {
2893             if (this.hasDefaultHeaders) {
2894                 for (var prop in this.defaultHeaders) {
2895                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2896                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2897                     }
2898                 }
2899             }
2900
2901             if (this.hasHeaders) {
2902                 for (var prop in this.headers) {
2903                     if (this.headers.hasOwnProperty(prop)) {
2904                         o.conn.setRequestHeader(prop, this.headers[prop]);
2905                     }
2906                 }
2907                 this.headers = {};
2908                 this.hasHeaders = false;
2909             }
2910         },
2911
2912         resetDefaultHeaders:function() {
2913             delete this.defaultHeaders;
2914             this.defaultHeaders = {};
2915             this.hasDefaultHeaders = false;
2916         },
2917
2918         abort:function(o, callback, isTimeout)
2919         {
2920             if(this.isCallInProgress(o)) {
2921                 o.conn.abort();
2922                 window.clearInterval(this.poll[o.tId]);
2923                 delete this.poll[o.tId];
2924                 if (isTimeout) {
2925                     delete this.timeout[o.tId];
2926                 }
2927
2928                 this.handleTransactionResponse(o, callback, true);
2929
2930                 return true;
2931             }
2932             else {
2933                 return false;
2934             }
2935         },
2936
2937
2938         isCallInProgress:function(o)
2939         {
2940             if (o && o.conn) {
2941                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2942             }
2943             else {
2944
2945                 return false;
2946             }
2947         },
2948
2949
2950         releaseObject:function(o)
2951         {
2952
2953             o.conn = null;
2954
2955             o = null;
2956         },
2957
2958         activeX:[
2959         'MSXML2.XMLHTTP.3.0',
2960         'MSXML2.XMLHTTP',
2961         'Microsoft.XMLHTTP'
2962         ]
2963
2964
2965     };
2966 })();/*
2967  * Portions of this file are based on pieces of Yahoo User Interface Library
2968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2969  * YUI licensed under the BSD License:
2970  * http://developer.yahoo.net/yui/license.txt
2971  * <script type="text/javascript">
2972  *
2973  */
2974
2975 Roo.lib.Region = function(t, r, b, l) {
2976     this.top = t;
2977     this[1] = t;
2978     this.right = r;
2979     this.bottom = b;
2980     this.left = l;
2981     this[0] = l;
2982 };
2983
2984
2985 Roo.lib.Region.prototype = {
2986     contains : function(region) {
2987         return ( region.left >= this.left &&
2988                  region.right <= this.right &&
2989                  region.top >= this.top &&
2990                  region.bottom <= this.bottom    );
2991
2992     },
2993
2994     getArea : function() {
2995         return ( (this.bottom - this.top) * (this.right - this.left) );
2996     },
2997
2998     intersect : function(region) {
2999         var t = Math.max(this.top, region.top);
3000         var r = Math.min(this.right, region.right);
3001         var b = Math.min(this.bottom, region.bottom);
3002         var l = Math.max(this.left, region.left);
3003
3004         if (b >= t && r >= l) {
3005             return new Roo.lib.Region(t, r, b, l);
3006         } else {
3007             return null;
3008         }
3009     },
3010     union : function(region) {
3011         var t = Math.min(this.top, region.top);
3012         var r = Math.max(this.right, region.right);
3013         var b = Math.max(this.bottom, region.bottom);
3014         var l = Math.min(this.left, region.left);
3015
3016         return new Roo.lib.Region(t, r, b, l);
3017     },
3018
3019     adjust : function(t, l, b, r) {
3020         this.top += t;
3021         this.left += l;
3022         this.right += r;
3023         this.bottom += b;
3024         return this;
3025     }
3026 };
3027
3028 Roo.lib.Region.getRegion = function(el) {
3029     var p = Roo.lib.Dom.getXY(el);
3030
3031     var t = p[1];
3032     var r = p[0] + el.offsetWidth;
3033     var b = p[1] + el.offsetHeight;
3034     var l = p[0];
3035
3036     return new Roo.lib.Region(t, r, b, l);
3037 };
3038 /*
3039  * Portions of this file are based on pieces of Yahoo User Interface Library
3040  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3041  * YUI licensed under the BSD License:
3042  * http://developer.yahoo.net/yui/license.txt
3043  * <script type="text/javascript">
3044  *
3045  */
3046 //@@dep Roo.lib.Region
3047
3048
3049 Roo.lib.Point = function(x, y) {
3050     if (x instanceof Array) {
3051         y = x[1];
3052         x = x[0];
3053     }
3054     this.x = this.right = this.left = this[0] = x;
3055     this.y = this.top = this.bottom = this[1] = y;
3056 };
3057
3058 Roo.lib.Point.prototype = new Roo.lib.Region();
3059 /*
3060  * Portions of this file are based on pieces of Yahoo User Interface Library
3061  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3062  * YUI licensed under the BSD License:
3063  * http://developer.yahoo.net/yui/license.txt
3064  * <script type="text/javascript">
3065  *
3066  */
3067  
3068 (function() {   
3069
3070     Roo.lib.Anim = {
3071         scroll : function(el, args, duration, easing, cb, scope) {
3072             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3073         },
3074
3075         motion : function(el, args, duration, easing, cb, scope) {
3076             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3077         },
3078
3079         color : function(el, args, duration, easing, cb, scope) {
3080             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3081         },
3082
3083         run : function(el, args, duration, easing, cb, scope, type) {
3084             type = type || Roo.lib.AnimBase;
3085             if (typeof easing == "string") {
3086                 easing = Roo.lib.Easing[easing];
3087             }
3088             var anim = new type(el, args, duration, easing);
3089             anim.animateX(function() {
3090                 Roo.callback(cb, scope);
3091             });
3092             return anim;
3093         }
3094     };
3095 })();/*
3096  * Portions of this file are based on pieces of Yahoo User Interface Library
3097  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3098  * YUI licensed under the BSD License:
3099  * http://developer.yahoo.net/yui/license.txt
3100  * <script type="text/javascript">
3101  *
3102  */
3103
3104 (function() {    
3105     var libFlyweight;
3106     
3107     function fly(el) {
3108         if (!libFlyweight) {
3109             libFlyweight = new Roo.Element.Flyweight();
3110         }
3111         libFlyweight.dom = el;
3112         return libFlyweight;
3113     }
3114
3115     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3116     
3117    
3118     
3119     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3120         if (el) {
3121             this.init(el, attributes, duration, method);
3122         }
3123     };
3124
3125     Roo.lib.AnimBase.fly = fly;
3126     
3127     
3128     
3129     Roo.lib.AnimBase.prototype = {
3130
3131         toString: function() {
3132             var el = this.getEl();
3133             var id = el.id || el.tagName;
3134             return ("Anim " + id);
3135         },
3136
3137         patterns: {
3138             noNegatives:        /width|height|opacity|padding/i,
3139             offsetAttribute:  /^((width|height)|(top|left))$/,
3140             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3141             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3142         },
3143
3144
3145         doMethod: function(attr, start, end) {
3146             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3147         },
3148
3149
3150         setAttribute: function(attr, val, unit) {
3151             if (this.patterns.noNegatives.test(attr)) {
3152                 val = (val > 0) ? val : 0;
3153             }
3154
3155             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3156         },
3157
3158
3159         getAttribute: function(attr) {
3160             var el = this.getEl();
3161             var val = fly(el).getStyle(attr);
3162
3163             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3164                 return parseFloat(val);
3165             }
3166
3167             var a = this.patterns.offsetAttribute.exec(attr) || [];
3168             var pos = !!( a[3] );
3169             var box = !!( a[2] );
3170
3171
3172             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3173                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3174             } else {
3175                 val = 0;
3176             }
3177
3178             return val;
3179         },
3180
3181
3182         getDefaultUnit: function(attr) {
3183             if (this.patterns.defaultUnit.test(attr)) {
3184                 return 'px';
3185             }
3186
3187             return '';
3188         },
3189
3190         animateX : function(callback, scope) {
3191             var f = function() {
3192                 this.onComplete.removeListener(f);
3193                 if (typeof callback == "function") {
3194                     callback.call(scope || this, this);
3195                 }
3196             };
3197             this.onComplete.addListener(f, this);
3198             this.animate();
3199         },
3200
3201
3202         setRuntimeAttribute: function(attr) {
3203             var start;
3204             var end;
3205             var attributes = this.attributes;
3206
3207             this.runtimeAttributes[attr] = {};
3208
3209             var isset = function(prop) {
3210                 return (typeof prop !== 'undefined');
3211             };
3212
3213             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3214                 return false;
3215             }
3216
3217             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3218
3219
3220             if (isset(attributes[attr]['to'])) {
3221                 end = attributes[attr]['to'];
3222             } else if (isset(attributes[attr]['by'])) {
3223                 if (start.constructor == Array) {
3224                     end = [];
3225                     for (var i = 0, len = start.length; i < len; ++i) {
3226                         end[i] = start[i] + attributes[attr]['by'][i];
3227                     }
3228                 } else {
3229                     end = start + attributes[attr]['by'];
3230                 }
3231             }
3232
3233             this.runtimeAttributes[attr].start = start;
3234             this.runtimeAttributes[attr].end = end;
3235
3236
3237             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3238         },
3239
3240
3241         init: function(el, attributes, duration, method) {
3242
3243             var isAnimated = false;
3244
3245
3246             var startTime = null;
3247
3248
3249             var actualFrames = 0;
3250
3251
3252             el = Roo.getDom(el);
3253
3254
3255             this.attributes = attributes || {};
3256
3257
3258             this.duration = duration || 1;
3259
3260
3261             this.method = method || Roo.lib.Easing.easeNone;
3262
3263
3264             this.useSeconds = true;
3265
3266
3267             this.currentFrame = 0;
3268
3269
3270             this.totalFrames = Roo.lib.AnimMgr.fps;
3271
3272
3273             this.getEl = function() {
3274                 return el;
3275             };
3276
3277
3278             this.isAnimated = function() {
3279                 return isAnimated;
3280             };
3281
3282
3283             this.getStartTime = function() {
3284                 return startTime;
3285             };
3286
3287             this.runtimeAttributes = {};
3288
3289
3290             this.animate = function() {
3291                 if (this.isAnimated()) {
3292                     return false;
3293                 }
3294
3295                 this.currentFrame = 0;
3296
3297                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3298
3299                 Roo.lib.AnimMgr.registerElement(this);
3300             };
3301
3302
3303             this.stop = function(finish) {
3304                 if (finish) {
3305                     this.currentFrame = this.totalFrames;
3306                     this._onTween.fire();
3307                 }
3308                 Roo.lib.AnimMgr.stop(this);
3309             };
3310
3311             var onStart = function() {
3312                 this.onStart.fire();
3313
3314                 this.runtimeAttributes = {};
3315                 for (var attr in this.attributes) {
3316                     this.setRuntimeAttribute(attr);
3317                 }
3318
3319                 isAnimated = true;
3320                 actualFrames = 0;
3321                 startTime = new Date();
3322             };
3323
3324
3325             var onTween = function() {
3326                 var data = {
3327                     duration: new Date() - this.getStartTime(),
3328                     currentFrame: this.currentFrame
3329                 };
3330
3331                 data.toString = function() {
3332                     return (
3333                             'duration: ' + data.duration +
3334                             ', currentFrame: ' + data.currentFrame
3335                             );
3336                 };
3337
3338                 this.onTween.fire(data);
3339
3340                 var runtimeAttributes = this.runtimeAttributes;
3341
3342                 for (var attr in runtimeAttributes) {
3343                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3344                 }
3345
3346                 actualFrames += 1;
3347             };
3348
3349             var onComplete = function() {
3350                 var actual_duration = (new Date() - startTime) / 1000 ;
3351
3352                 var data = {
3353                     duration: actual_duration,
3354                     frames: actualFrames,
3355                     fps: actualFrames / actual_duration
3356                 };
3357
3358                 data.toString = function() {
3359                     return (
3360                             'duration: ' + data.duration +
3361                             ', frames: ' + data.frames +
3362                             ', fps: ' + data.fps
3363                             );
3364                 };
3365
3366                 isAnimated = false;
3367                 actualFrames = 0;
3368                 this.onComplete.fire(data);
3369             };
3370
3371
3372             this._onStart = new Roo.util.Event(this);
3373             this.onStart = new Roo.util.Event(this);
3374             this.onTween = new Roo.util.Event(this);
3375             this._onTween = new Roo.util.Event(this);
3376             this.onComplete = new Roo.util.Event(this);
3377             this._onComplete = new Roo.util.Event(this);
3378             this._onStart.addListener(onStart);
3379             this._onTween.addListener(onTween);
3380             this._onComplete.addListener(onComplete);
3381         }
3382     };
3383 })();
3384 /*
3385  * Portions of this file are based on pieces of Yahoo User Interface Library
3386  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3387  * YUI licensed under the BSD License:
3388  * http://developer.yahoo.net/yui/license.txt
3389  * <script type="text/javascript">
3390  *
3391  */
3392
3393 Roo.lib.AnimMgr = new function() {
3394
3395         var thread = null;
3396
3397
3398         var queue = [];
3399
3400
3401         var tweenCount = 0;
3402
3403
3404         this.fps = 1000;
3405
3406
3407         this.delay = 1;
3408
3409
3410         this.registerElement = function(tween) {
3411             queue[queue.length] = tween;
3412             tweenCount += 1;
3413             tween._onStart.fire();
3414             this.start();
3415         };
3416
3417
3418         this.unRegister = function(tween, index) {
3419             tween._onComplete.fire();
3420             index = index || getIndex(tween);
3421             if (index != -1) {
3422                 queue.splice(index, 1);
3423             }
3424
3425             tweenCount -= 1;
3426             if (tweenCount <= 0) {
3427                 this.stop();
3428             }
3429         };
3430
3431
3432         this.start = function() {
3433             if (thread === null) {
3434                 thread = setInterval(this.run, this.delay);
3435             }
3436         };
3437
3438
3439         this.stop = function(tween) {
3440             if (!tween) {
3441                 clearInterval(thread);
3442
3443                 for (var i = 0, len = queue.length; i < len; ++i) {
3444                     if (queue[0].isAnimated()) {
3445                         this.unRegister(queue[0], 0);
3446                     }
3447                 }
3448
3449                 queue = [];
3450                 thread = null;
3451                 tweenCount = 0;
3452             }
3453             else {
3454                 this.unRegister(tween);
3455             }
3456         };
3457
3458
3459         this.run = function() {
3460             for (var i = 0, len = queue.length; i < len; ++i) {
3461                 var tween = queue[i];
3462                 if (!tween || !tween.isAnimated()) {
3463                     continue;
3464                 }
3465
3466                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3467                 {
3468                     tween.currentFrame += 1;
3469
3470                     if (tween.useSeconds) {
3471                         correctFrame(tween);
3472                     }
3473                     tween._onTween.fire();
3474                 }
3475                 else {
3476                     Roo.lib.AnimMgr.stop(tween, i);
3477                 }
3478             }
3479         };
3480
3481         var getIndex = function(anim) {
3482             for (var i = 0, len = queue.length; i < len; ++i) {
3483                 if (queue[i] == anim) {
3484                     return i;
3485                 }
3486             }
3487             return -1;
3488         };
3489
3490
3491         var correctFrame = function(tween) {
3492             var frames = tween.totalFrames;
3493             var frame = tween.currentFrame;
3494             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3495             var elapsed = (new Date() - tween.getStartTime());
3496             var tweak = 0;
3497
3498             if (elapsed < tween.duration * 1000) {
3499                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3500             } else {
3501                 tweak = frames - (frame + 1);
3502             }
3503             if (tweak > 0 && isFinite(tweak)) {
3504                 if (tween.currentFrame + tweak >= frames) {
3505                     tweak = frames - (frame + 1);
3506                 }
3507
3508                 tween.currentFrame += tweak;
3509             }
3510         };
3511     };/*
3512  * Portions of this file are based on pieces of Yahoo User Interface Library
3513  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3514  * YUI licensed under the BSD License:
3515  * http://developer.yahoo.net/yui/license.txt
3516  * <script type="text/javascript">
3517  *
3518  */
3519 Roo.lib.Bezier = new function() {
3520
3521         this.getPosition = function(points, t) {
3522             var n = points.length;
3523             var tmp = [];
3524
3525             for (var i = 0; i < n; ++i) {
3526                 tmp[i] = [points[i][0], points[i][1]];
3527             }
3528
3529             for (var j = 1; j < n; ++j) {
3530                 for (i = 0; i < n - j; ++i) {
3531                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3532                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3533                 }
3534             }
3535
3536             return [ tmp[0][0], tmp[0][1] ];
3537
3538         };
3539     };/*
3540  * Portions of this file are based on pieces of Yahoo User Interface Library
3541  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3542  * YUI licensed under the BSD License:
3543  * http://developer.yahoo.net/yui/license.txt
3544  * <script type="text/javascript">
3545  *
3546  */
3547 (function() {
3548
3549     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3550         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3551     };
3552
3553     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3554
3555     var fly = Roo.lib.AnimBase.fly;
3556     var Y = Roo.lib;
3557     var superclass = Y.ColorAnim.superclass;
3558     var proto = Y.ColorAnim.prototype;
3559
3560     proto.toString = function() {
3561         var el = this.getEl();
3562         var id = el.id || el.tagName;
3563         return ("ColorAnim " + id);
3564     };
3565
3566     proto.patterns.color = /color$/i;
3567     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3568     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3569     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3570     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3571
3572
3573     proto.parseColor = function(s) {
3574         if (s.length == 3) {
3575             return s;
3576         }
3577
3578         var c = this.patterns.hex.exec(s);
3579         if (c && c.length == 4) {
3580             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3581         }
3582
3583         c = this.patterns.rgb.exec(s);
3584         if (c && c.length == 4) {
3585             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3586         }
3587
3588         c = this.patterns.hex3.exec(s);
3589         if (c && c.length == 4) {
3590             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3591         }
3592
3593         return null;
3594     };
3595     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3596     proto.getAttribute = function(attr) {
3597         var el = this.getEl();
3598         if (this.patterns.color.test(attr)) {
3599             var val = fly(el).getStyle(attr);
3600
3601             if (this.patterns.transparent.test(val)) {
3602                 var parent = el.parentNode;
3603                 val = fly(parent).getStyle(attr);
3604
3605                 while (parent && this.patterns.transparent.test(val)) {
3606                     parent = parent.parentNode;
3607                     val = fly(parent).getStyle(attr);
3608                     if (parent.tagName.toUpperCase() == 'HTML') {
3609                         val = '#fff';
3610                     }
3611                 }
3612             }
3613         } else {
3614             val = superclass.getAttribute.call(this, attr);
3615         }
3616
3617         return val;
3618     };
3619     proto.getAttribute = function(attr) {
3620         var el = this.getEl();
3621         if (this.patterns.color.test(attr)) {
3622             var val = fly(el).getStyle(attr);
3623
3624             if (this.patterns.transparent.test(val)) {
3625                 var parent = el.parentNode;
3626                 val = fly(parent).getStyle(attr);
3627
3628                 while (parent && this.patterns.transparent.test(val)) {
3629                     parent = parent.parentNode;
3630                     val = fly(parent).getStyle(attr);
3631                     if (parent.tagName.toUpperCase() == 'HTML') {
3632                         val = '#fff';
3633                     }
3634                 }
3635             }
3636         } else {
3637             val = superclass.getAttribute.call(this, attr);
3638         }
3639
3640         return val;
3641     };
3642
3643     proto.doMethod = function(attr, start, end) {
3644         var val;
3645
3646         if (this.patterns.color.test(attr)) {
3647             val = [];
3648             for (var i = 0, len = start.length; i < len; ++i) {
3649                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3650             }
3651
3652             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3653         }
3654         else {
3655             val = superclass.doMethod.call(this, attr, start, end);
3656         }
3657
3658         return val;
3659     };
3660
3661     proto.setRuntimeAttribute = function(attr) {
3662         superclass.setRuntimeAttribute.call(this, attr);
3663
3664         if (this.patterns.color.test(attr)) {
3665             var attributes = this.attributes;
3666             var start = this.parseColor(this.runtimeAttributes[attr].start);
3667             var end = this.parseColor(this.runtimeAttributes[attr].end);
3668
3669             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3670                 end = this.parseColor(attributes[attr].by);
3671
3672                 for (var i = 0, len = start.length; i < len; ++i) {
3673                     end[i] = start[i] + end[i];
3674                 }
3675             }
3676
3677             this.runtimeAttributes[attr].start = start;
3678             this.runtimeAttributes[attr].end = end;
3679         }
3680     };
3681 })();
3682
3683 /*
3684  * Portions of this file are based on pieces of Yahoo User Interface Library
3685  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3686  * YUI licensed under the BSD License:
3687  * http://developer.yahoo.net/yui/license.txt
3688  * <script type="text/javascript">
3689  *
3690  */
3691 Roo.lib.Easing = {
3692
3693
3694     easeNone: function (t, b, c, d) {
3695         return c * t / d + b;
3696     },
3697
3698
3699     easeIn: function (t, b, c, d) {
3700         return c * (t /= d) * t + b;
3701     },
3702
3703
3704     easeOut: function (t, b, c, d) {
3705         return -c * (t /= d) * (t - 2) + b;
3706     },
3707
3708
3709     easeBoth: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t + b;
3712         }
3713
3714         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3715     },
3716
3717
3718     easeInStrong: function (t, b, c, d) {
3719         return c * (t /= d) * t * t * t + b;
3720     },
3721
3722
3723     easeOutStrong: function (t, b, c, d) {
3724         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3725     },
3726
3727
3728     easeBothStrong: function (t, b, c, d) {
3729         if ((t /= d / 2) < 1) {
3730             return c / 2 * t * t * t * t + b;
3731         }
3732
3733         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3734     },
3735
3736
3737
3738     elasticIn: function (t, b, c, d, a, p) {
3739         if (t == 0) {
3740             return b;
3741         }
3742         if ((t /= d) == 1) {
3743             return b + c;
3744         }
3745         if (!p) {
3746             p = d * .3;
3747         }
3748
3749         if (!a || a < Math.abs(c)) {
3750             a = c;
3751             var s = p / 4;
3752         }
3753         else {
3754             var s = p / (2 * Math.PI) * Math.asin(c / a);
3755         }
3756
3757         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3758     },
3759
3760
3761     elasticOut: function (t, b, c, d, a, p) {
3762         if (t == 0) {
3763             return b;
3764         }
3765         if ((t /= d) == 1) {
3766             return b + c;
3767         }
3768         if (!p) {
3769             p = d * .3;
3770         }
3771
3772         if (!a || a < Math.abs(c)) {
3773             a = c;
3774             var s = p / 4;
3775         }
3776         else {
3777             var s = p / (2 * Math.PI) * Math.asin(c / a);
3778         }
3779
3780         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3781     },
3782
3783
3784     elasticBoth: function (t, b, c, d, a, p) {
3785         if (t == 0) {
3786             return b;
3787         }
3788
3789         if ((t /= d / 2) == 2) {
3790             return b + c;
3791         }
3792
3793         if (!p) {
3794             p = d * (.3 * 1.5);
3795         }
3796
3797         if (!a || a < Math.abs(c)) {
3798             a = c;
3799             var s = p / 4;
3800         }
3801         else {
3802             var s = p / (2 * Math.PI) * Math.asin(c / a);
3803         }
3804
3805         if (t < 1) {
3806             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3807                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3808         }
3809         return a * Math.pow(2, -10 * (t -= 1)) *
3810                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3811     },
3812
3813
3814
3815     backIn: function (t, b, c, d, s) {
3816         if (typeof s == 'undefined') {
3817             s = 1.70158;
3818         }
3819         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3820     },
3821
3822
3823     backOut: function (t, b, c, d, s) {
3824         if (typeof s == 'undefined') {
3825             s = 1.70158;
3826         }
3827         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3828     },
3829
3830
3831     backBoth: function (t, b, c, d, s) {
3832         if (typeof s == 'undefined') {
3833             s = 1.70158;
3834         }
3835
3836         if ((t /= d / 2 ) < 1) {
3837             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3838         }
3839         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3840     },
3841
3842
3843     bounceIn: function (t, b, c, d) {
3844         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3845     },
3846
3847
3848     bounceOut: function (t, b, c, d) {
3849         if ((t /= d) < (1 / 2.75)) {
3850             return c * (7.5625 * t * t) + b;
3851         } else if (t < (2 / 2.75)) {
3852             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3853         } else if (t < (2.5 / 2.75)) {
3854             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3855         }
3856         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3857     },
3858
3859
3860     bounceBoth: function (t, b, c, d) {
3861         if (t < d / 2) {
3862             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3863         }
3864         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3865     }
3866 };/*
3867  * Portions of this file are based on pieces of Yahoo User Interface Library
3868  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3869  * YUI licensed under the BSD License:
3870  * http://developer.yahoo.net/yui/license.txt
3871  * <script type="text/javascript">
3872  *
3873  */
3874     (function() {
3875         Roo.lib.Motion = function(el, attributes, duration, method) {
3876             if (el) {
3877                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3878             }
3879         };
3880
3881         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3882
3883
3884         var Y = Roo.lib;
3885         var superclass = Y.Motion.superclass;
3886         var proto = Y.Motion.prototype;
3887
3888         proto.toString = function() {
3889             var el = this.getEl();
3890             var id = el.id || el.tagName;
3891             return ("Motion " + id);
3892         };
3893
3894         proto.patterns.points = /^points$/i;
3895
3896         proto.setAttribute = function(attr, val, unit) {
3897             if (this.patterns.points.test(attr)) {
3898                 unit = unit || 'px';
3899                 superclass.setAttribute.call(this, 'left', val[0], unit);
3900                 superclass.setAttribute.call(this, 'top', val[1], unit);
3901             } else {
3902                 superclass.setAttribute.call(this, attr, val, unit);
3903             }
3904         };
3905
3906         proto.getAttribute = function(attr) {
3907             if (this.patterns.points.test(attr)) {
3908                 var val = [
3909                         superclass.getAttribute.call(this, 'left'),
3910                         superclass.getAttribute.call(this, 'top')
3911                         ];
3912             } else {
3913                 val = superclass.getAttribute.call(this, attr);
3914             }
3915
3916             return val;
3917         };
3918
3919         proto.doMethod = function(attr, start, end) {
3920             var val = null;
3921
3922             if (this.patterns.points.test(attr)) {
3923                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3924                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3925             } else {
3926                 val = superclass.doMethod.call(this, attr, start, end);
3927             }
3928             return val;
3929         };
3930
3931         proto.setRuntimeAttribute = function(attr) {
3932             if (this.patterns.points.test(attr)) {
3933                 var el = this.getEl();
3934                 var attributes = this.attributes;
3935                 var start;
3936                 var control = attributes['points']['control'] || [];
3937                 var end;
3938                 var i, len;
3939
3940                 if (control.length > 0 && !(control[0] instanceof Array)) {
3941                     control = [control];
3942                 } else {
3943                     var tmp = [];
3944                     for (i = 0,len = control.length; i < len; ++i) {
3945                         tmp[i] = control[i];
3946                     }
3947                     control = tmp;
3948                 }
3949
3950                 Roo.fly(el).position();
3951
3952                 if (isset(attributes['points']['from'])) {
3953                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3954                 }
3955                 else {
3956                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3957                 }
3958
3959                 start = this.getAttribute('points');
3960
3961
3962                 if (isset(attributes['points']['to'])) {
3963                     end = translateValues.call(this, attributes['points']['to'], start);
3964
3965                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3966                     for (i = 0,len = control.length; i < len; ++i) {
3967                         control[i] = translateValues.call(this, control[i], start);
3968                     }
3969
3970
3971                 } else if (isset(attributes['points']['by'])) {
3972                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3973
3974                     for (i = 0,len = control.length; i < len; ++i) {
3975                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3976                     }
3977                 }
3978
3979                 this.runtimeAttributes[attr] = [start];
3980
3981                 if (control.length > 0) {
3982                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3983                 }
3984
3985                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3986             }
3987             else {
3988                 superclass.setRuntimeAttribute.call(this, attr);
3989             }
3990         };
3991
3992         var translateValues = function(val, start) {
3993             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3994             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3995
3996             return val;
3997         };
3998
3999         var isset = function(prop) {
4000             return (typeof prop !== 'undefined');
4001         };
4002     })();
4003 /*
4004  * Portions of this file are based on pieces of Yahoo User Interface Library
4005  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4006  * YUI licensed under the BSD License:
4007  * http://developer.yahoo.net/yui/license.txt
4008  * <script type="text/javascript">
4009  *
4010  */
4011     (function() {
4012         Roo.lib.Scroll = function(el, attributes, duration, method) {
4013             if (el) {
4014                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4015             }
4016         };
4017
4018         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4019
4020
4021         var Y = Roo.lib;
4022         var superclass = Y.Scroll.superclass;
4023         var proto = Y.Scroll.prototype;
4024
4025         proto.toString = function() {
4026             var el = this.getEl();
4027             var id = el.id || el.tagName;
4028             return ("Scroll " + id);
4029         };
4030
4031         proto.doMethod = function(attr, start, end) {
4032             var val = null;
4033
4034             if (attr == 'scroll') {
4035                 val = [
4036                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4037                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4038                         ];
4039
4040             } else {
4041                 val = superclass.doMethod.call(this, attr, start, end);
4042             }
4043             return val;
4044         };
4045
4046         proto.getAttribute = function(attr) {
4047             var val = null;
4048             var el = this.getEl();
4049
4050             if (attr == 'scroll') {
4051                 val = [ el.scrollLeft, el.scrollTop ];
4052             } else {
4053                 val = superclass.getAttribute.call(this, attr);
4054             }
4055
4056             return val;
4057         };
4058
4059         proto.setAttribute = function(attr, val, unit) {
4060             var el = this.getEl();
4061
4062             if (attr == 'scroll') {
4063                 el.scrollLeft = val[0];
4064                 el.scrollTop = val[1];
4065             } else {
4066                 superclass.setAttribute.call(this, attr, val, unit);
4067             }
4068         };
4069     })();
4070 /*
4071  * Based on:
4072  * Ext JS Library 1.1.1
4073  * Copyright(c) 2006-2007, Ext JS, LLC.
4074  *
4075  * Originally Released Under LGPL - original licence link has changed is not relivant.
4076  *
4077  * Fork - LGPL
4078  * <script type="text/javascript">
4079  */
4080
4081
4082 // nasty IE9 hack - what a pile of crap that is..
4083
4084  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4085     Range.prototype.createContextualFragment = function (html) {
4086         var doc = window.document;
4087         var container = doc.createElement("div");
4088         container.innerHTML = html;
4089         var frag = doc.createDocumentFragment(), n;
4090         while ((n = container.firstChild)) {
4091             frag.appendChild(n);
4092         }
4093         return frag;
4094     };
4095 }
4096
4097 /**
4098  * @class Roo.DomHelper
4099  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4100  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4101  * @singleton
4102  */
4103 Roo.DomHelper = function(){
4104     var tempTableEl = null;
4105     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4106     var tableRe = /^table|tbody|tr|td$/i;
4107     var xmlns = {};
4108     // build as innerHTML where available
4109     /** @ignore */
4110     var createHtml = function(o){
4111         if(typeof o == 'string'){
4112             return o;
4113         }
4114         var b = "";
4115         if(!o.tag){
4116             o.tag = "div";
4117         }
4118         b += "<" + o.tag;
4119         for(var attr in o){
4120             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4121             if(attr == "style"){
4122                 var s = o["style"];
4123                 if(typeof s == "function"){
4124                     s = s.call();
4125                 }
4126                 if(typeof s == "string"){
4127                     b += ' style="' + s + '"';
4128                 }else if(typeof s == "object"){
4129                     b += ' style="';
4130                     for(var key in s){
4131                         if(typeof s[key] != "function"){
4132                             b += key + ":" + s[key] + ";";
4133                         }
4134                     }
4135                     b += '"';
4136                 }
4137             }else{
4138                 if(attr == "cls"){
4139                     b += ' class="' + o["cls"] + '"';
4140                 }else if(attr == "htmlFor"){
4141                     b += ' for="' + o["htmlFor"] + '"';
4142                 }else{
4143                     b += " " + attr + '="' + o[attr] + '"';
4144                 }
4145             }
4146         }
4147         if(emptyTags.test(o.tag)){
4148             b += "/>";
4149         }else{
4150             b += ">";
4151             var cn = o.children || o.cn;
4152             if(cn){
4153                 //http://bugs.kde.org/show_bug.cgi?id=71506
4154                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4155                     for(var i = 0, len = cn.length; i < len; i++) {
4156                         b += createHtml(cn[i], b);
4157                     }
4158                 }else{
4159                     b += createHtml(cn, b);
4160                 }
4161             }
4162             if(o.html){
4163                 b += o.html;
4164             }
4165             b += "</" + o.tag + ">";
4166         }
4167         return b;
4168     };
4169
4170     // build as dom
4171     /** @ignore */
4172     var createDom = function(o, parentNode){
4173          
4174         // defininition craeted..
4175         var ns = false;
4176         if (o.ns && o.ns != 'html') {
4177                
4178             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4179                 xmlns[o.ns] = o.xmlns;
4180                 ns = o.xmlns;
4181             }
4182             if (typeof(xmlns[o.ns]) == 'undefined') {
4183                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4184             }
4185             ns = xmlns[o.ns];
4186         }
4187         
4188         
4189         if (typeof(o) == 'string') {
4190             return parentNode.appendChild(document.createTextNode(o));
4191         }
4192         o.tag = o.tag || div;
4193         if (o.ns && Roo.isIE) {
4194             ns = false;
4195             o.tag = o.ns + ':' + o.tag;
4196             
4197         }
4198         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4199         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4200         for(var attr in o){
4201             
4202             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4203                     attr == "style" || typeof o[attr] == "function") continue;
4204                     
4205             if(attr=="cls" && Roo.isIE){
4206                 el.className = o["cls"];
4207             }else{
4208                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4209                 else el[attr] = o[attr];
4210             }
4211         }
4212         Roo.DomHelper.applyStyles(el, o.style);
4213         var cn = o.children || o.cn;
4214         if(cn){
4215             //http://bugs.kde.org/show_bug.cgi?id=71506
4216              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4217                 for(var i = 0, len = cn.length; i < len; i++) {
4218                     createDom(cn[i], el);
4219                 }
4220             }else{
4221                 createDom(cn, el);
4222             }
4223         }
4224         if(o.html){
4225             el.innerHTML = o.html;
4226         }
4227         if(parentNode){
4228            parentNode.appendChild(el);
4229         }
4230         return el;
4231     };
4232
4233     var ieTable = function(depth, s, h, e){
4234         tempTableEl.innerHTML = [s, h, e].join('');
4235         var i = -1, el = tempTableEl;
4236         while(++i < depth){
4237             el = el.firstChild;
4238         }
4239         return el;
4240     };
4241
4242     // kill repeat to save bytes
4243     var ts = '<table>',
4244         te = '</table>',
4245         tbs = ts+'<tbody>',
4246         tbe = '</tbody>'+te,
4247         trs = tbs + '<tr>',
4248         tre = '</tr>'+tbe;
4249
4250     /**
4251      * @ignore
4252      * Nasty code for IE's broken table implementation
4253      */
4254     var insertIntoTable = function(tag, where, el, html){
4255         if(!tempTableEl){
4256             tempTableEl = document.createElement('div');
4257         }
4258         var node;
4259         var before = null;
4260         if(tag == 'td'){
4261             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4262                 return;
4263             }
4264             if(where == 'beforebegin'){
4265                 before = el;
4266                 el = el.parentNode;
4267             } else{
4268                 before = el.nextSibling;
4269                 el = el.parentNode;
4270             }
4271             node = ieTable(4, trs, html, tre);
4272         }
4273         else if(tag == 'tr'){
4274             if(where == 'beforebegin'){
4275                 before = el;
4276                 el = el.parentNode;
4277                 node = ieTable(3, tbs, html, tbe);
4278             } else if(where == 'afterend'){
4279                 before = el.nextSibling;
4280                 el = el.parentNode;
4281                 node = ieTable(3, tbs, html, tbe);
4282             } else{ // INTO a TR
4283                 if(where == 'afterbegin'){
4284                     before = el.firstChild;
4285                 }
4286                 node = ieTable(4, trs, html, tre);
4287             }
4288         } else if(tag == 'tbody'){
4289             if(where == 'beforebegin'){
4290                 before = el;
4291                 el = el.parentNode;
4292                 node = ieTable(2, ts, html, te);
4293             } else if(where == 'afterend'){
4294                 before = el.nextSibling;
4295                 el = el.parentNode;
4296                 node = ieTable(2, ts, html, te);
4297             } else{
4298                 if(where == 'afterbegin'){
4299                     before = el.firstChild;
4300                 }
4301                 node = ieTable(3, tbs, html, tbe);
4302             }
4303         } else{ // TABLE
4304             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4305                 return;
4306             }
4307             if(where == 'afterbegin'){
4308                 before = el.firstChild;
4309             }
4310             node = ieTable(2, ts, html, te);
4311         }
4312         el.insertBefore(node, before);
4313         return node;
4314     };
4315
4316     return {
4317     /** True to force the use of DOM instead of html fragments @type Boolean */
4318     useDom : false,
4319
4320     /**
4321      * Returns the markup for the passed Element(s) config
4322      * @param {Object} o The Dom object spec (and children)
4323      * @return {String}
4324      */
4325     markup : function(o){
4326         return createHtml(o);
4327     },
4328
4329     /**
4330      * Applies a style specification to an element
4331      * @param {String/HTMLElement} el The element to apply styles to
4332      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4333      * a function which returns such a specification.
4334      */
4335     applyStyles : function(el, styles){
4336         if(styles){
4337            el = Roo.fly(el);
4338            if(typeof styles == "string"){
4339                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4340                var matches;
4341                while ((matches = re.exec(styles)) != null){
4342                    el.setStyle(matches[1], matches[2]);
4343                }
4344            }else if (typeof styles == "object"){
4345                for (var style in styles){
4346                   el.setStyle(style, styles[style]);
4347                }
4348            }else if (typeof styles == "function"){
4349                 Roo.DomHelper.applyStyles(el, styles.call());
4350            }
4351         }
4352     },
4353
4354     /**
4355      * Inserts an HTML fragment into the Dom
4356      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4357      * @param {HTMLElement} el The context element
4358      * @param {String} html The HTML fragmenet
4359      * @return {HTMLElement} The new node
4360      */
4361     insertHtml : function(where, el, html){
4362         where = where.toLowerCase();
4363         if(el.insertAdjacentHTML){
4364             if(tableRe.test(el.tagName)){
4365                 var rs;
4366                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4367                     return rs;
4368                 }
4369             }
4370             switch(where){
4371                 case "beforebegin":
4372                     el.insertAdjacentHTML('BeforeBegin', html);
4373                     return el.previousSibling;
4374                 case "afterbegin":
4375                     el.insertAdjacentHTML('AfterBegin', html);
4376                     return el.firstChild;
4377                 case "beforeend":
4378                     el.insertAdjacentHTML('BeforeEnd', html);
4379                     return el.lastChild;
4380                 case "afterend":
4381                     el.insertAdjacentHTML('AfterEnd', html);
4382                     return el.nextSibling;
4383             }
4384             throw 'Illegal insertion point -> "' + where + '"';
4385         }
4386         var range = el.ownerDocument.createRange();
4387         var frag;
4388         switch(where){
4389              case "beforebegin":
4390                 range.setStartBefore(el);
4391                 frag = range.createContextualFragment(html);
4392                 el.parentNode.insertBefore(frag, el);
4393                 return el.previousSibling;
4394              case "afterbegin":
4395                 if(el.firstChild){
4396                     range.setStartBefore(el.firstChild);
4397                     frag = range.createContextualFragment(html);
4398                     el.insertBefore(frag, el.firstChild);
4399                     return el.firstChild;
4400                 }else{
4401                     el.innerHTML = html;
4402                     return el.firstChild;
4403                 }
4404             case "beforeend":
4405                 if(el.lastChild){
4406                     range.setStartAfter(el.lastChild);
4407                     frag = range.createContextualFragment(html);
4408                     el.appendChild(frag);
4409                     return el.lastChild;
4410                 }else{
4411                     el.innerHTML = html;
4412                     return el.lastChild;
4413                 }
4414             case "afterend":
4415                 range.setStartAfter(el);
4416                 frag = range.createContextualFragment(html);
4417                 el.parentNode.insertBefore(frag, el.nextSibling);
4418                 return el.nextSibling;
4419             }
4420             throw 'Illegal insertion point -> "' + where + '"';
4421     },
4422
4423     /**
4424      * Creates new Dom element(s) and inserts them before el
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     insertBefore : function(el, o, returnElement){
4431         return this.doInsert(el, o, returnElement, "beforeBegin");
4432     },
4433
4434     /**
4435      * Creates new Dom element(s) and inserts them after el
4436      * @param {String/HTMLElement/Element} el The context element
4437      * @param {Object} o The Dom object spec (and children)
4438      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4439      * @return {HTMLElement/Roo.Element} The new node
4440      */
4441     insertAfter : function(el, o, returnElement){
4442         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and inserts them as the first child of el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     insertFirst : function(el, o, returnElement){
4453         return this.doInsert(el, o, returnElement, "afterBegin");
4454     },
4455
4456     // private
4457     doInsert : function(el, o, returnElement, pos, sibling){
4458         el = Roo.getDom(el);
4459         var newNode;
4460         if(this.useDom || o.ns){
4461             newNode = createDom(o, null);
4462             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4463         }else{
4464             var html = createHtml(o);
4465             newNode = this.insertHtml(pos, el, html);
4466         }
4467         return returnElement ? Roo.get(newNode, true) : newNode;
4468     },
4469
4470     /**
4471      * Creates new Dom element(s) and appends them to el
4472      * @param {String/HTMLElement/Element} el The context element
4473      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4474      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4475      * @return {HTMLElement/Roo.Element} The new node
4476      */
4477     append : function(el, o, returnElement){
4478         el = Roo.getDom(el);
4479         var newNode;
4480         if(this.useDom || o.ns){
4481             newNode = createDom(o, null);
4482             el.appendChild(newNode);
4483         }else{
4484             var html = createHtml(o);
4485             newNode = this.insertHtml("beforeEnd", el, html);
4486         }
4487         return returnElement ? Roo.get(newNode, true) : newNode;
4488     },
4489
4490     /**
4491      * Creates new Dom element(s) and overwrites the contents of el with them
4492      * @param {String/HTMLElement/Element} el The context element
4493      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4494      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4495      * @return {HTMLElement/Roo.Element} The new node
4496      */
4497     overwrite : function(el, o, returnElement){
4498         el = Roo.getDom(el);
4499         if (o.ns) {
4500           
4501             while (el.childNodes.length) {
4502                 el.removeChild(el.firstChild);
4503             }
4504             createDom(o, el);
4505         } else {
4506             el.innerHTML = createHtml(o);   
4507         }
4508         
4509         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4510     },
4511
4512     /**
4513      * Creates a new Roo.DomHelper.Template from the Dom object spec
4514      * @param {Object} o The Dom object spec (and children)
4515      * @return {Roo.DomHelper.Template} The new template
4516      */
4517     createTemplate : function(o){
4518         var html = createHtml(o);
4519         return new Roo.Template(html);
4520     }
4521     };
4522 }();
4523 /*
4524  * Based on:
4525  * Ext JS Library 1.1.1
4526  * Copyright(c) 2006-2007, Ext JS, LLC.
4527  *
4528  * Originally Released Under LGPL - original licence link has changed is not relivant.
4529  *
4530  * Fork - LGPL
4531  * <script type="text/javascript">
4532  */
4533  
4534 /**
4535 * @class Roo.Template
4536 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4537 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4538 * Usage:
4539 <pre><code>
4540 var t = new Roo.Template({
4541     html :  '&lt;div name="{id}"&gt;' + 
4542         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4543         '&lt;/div&gt;',
4544     myformat: function (value, allValues) {
4545         return 'XX' + value;
4546     }
4547 });
4548 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4549 </code></pre>
4550 * For more information see this blog post with examples:
4551 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4552      - Create Elements using DOM, HTML fragments and Templates</a>. 
4553 * @constructor
4554 * @param {Object} cfg - Configuration object.
4555 */
4556 Roo.Template = function(cfg){
4557     // BC!
4558     if(cfg instanceof Array){
4559         cfg = cfg.join("");
4560     }else if(arguments.length > 1){
4561         cfg = Array.prototype.join.call(arguments, "");
4562     }
4563     
4564     
4565     if (typeof(cfg) == 'object') {
4566         Roo.apply(this,cfg)
4567     } else {
4568         // bc
4569         this.html = cfg;
4570     }
4571     if (this.url) {
4572         this.load();
4573     }
4574     
4575 };
4576 Roo.Template.prototype = {
4577     
4578     /**
4579      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4580      *                    it should be fixed so that template is observable...
4581      */
4582     url : false,
4583     /**
4584      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4585      */
4586     html : '',
4587     /**
4588      * Returns an HTML fragment of this template with the specified values applied.
4589      * @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'})
4590      * @return {String} The HTML fragment
4591      */
4592     applyTemplate : function(values){
4593         try {
4594            
4595             if(this.compiled){
4596                 return this.compiled(values);
4597             }
4598             var useF = this.disableFormats !== true;
4599             var fm = Roo.util.Format, tpl = this;
4600             var fn = function(m, name, format, args){
4601                 if(format && useF){
4602                     if(format.substr(0, 5) == "this."){
4603                         return tpl.call(format.substr(5), values[name], values);
4604                     }else{
4605                         if(args){
4606                             // quoted values are required for strings in compiled templates, 
4607                             // but for non compiled we need to strip them
4608                             // quoted reversed for jsmin
4609                             var re = /^\s*['"](.*)["']\s*$/;
4610                             args = args.split(',');
4611                             for(var i = 0, len = args.length; i < len; i++){
4612                                 args[i] = args[i].replace(re, "$1");
4613                             }
4614                             args = [values[name]].concat(args);
4615                         }else{
4616                             args = [values[name]];
4617                         }
4618                         return fm[format].apply(fm, args);
4619                     }
4620                 }else{
4621                     return values[name] !== undefined ? values[name] : "";
4622                 }
4623             };
4624             return this.html.replace(this.re, fn);
4625         } catch (e) {
4626             Roo.log(e);
4627             throw e;
4628         }
4629          
4630     },
4631     
4632     loading : false,
4633       
4634     load : function ()
4635     {
4636          
4637         if (this.loading) {
4638             return;
4639         }
4640         var _t = this;
4641         
4642         this.loading = true;
4643         this.compiled = false;
4644         
4645         var cx = new Roo.data.Connection();
4646         cx.request({
4647             url : this.url,
4648             method : 'GET',
4649             success : function (response) {
4650                 _t.loading = false;
4651                 _t.html = response.responseText;
4652                 _t.url = false;
4653                 _t.compile();
4654              },
4655             failure : function(response) {
4656                 Roo.log("Template failed to load from " + url);
4657                 _t.loading = false;
4658             }
4659         });
4660     },
4661
4662     /**
4663      * Sets the HTML used as the template and optionally compiles it.
4664      * @param {String} html
4665      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4666      * @return {Roo.Template} this
4667      */
4668     set : function(html, compile){
4669         this.html = html;
4670         this.compiled = null;
4671         if(compile){
4672             this.compile();
4673         }
4674         return this;
4675     },
4676     
4677     /**
4678      * True to disable format functions (defaults to false)
4679      * @type Boolean
4680      */
4681     disableFormats : false,
4682     
4683     /**
4684     * The regular expression used to match template variables 
4685     * @type RegExp
4686     * @property 
4687     */
4688     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4689     
4690     /**
4691      * Compiles the template into an internal function, eliminating the RegEx overhead.
4692      * @return {Roo.Template} this
4693      */
4694     compile : function(){
4695         var fm = Roo.util.Format;
4696         var useF = this.disableFormats !== true;
4697         var sep = Roo.isGecko ? "+" : ",";
4698         var fn = function(m, name, format, args){
4699             if(format && useF){
4700                 args = args ? ',' + args : "";
4701                 if(format.substr(0, 5) != "this."){
4702                     format = "fm." + format + '(';
4703                 }else{
4704                     format = 'this.call("'+ format.substr(5) + '", ';
4705                     args = ", values";
4706                 }
4707             }else{
4708                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4709             }
4710             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4711         };
4712         var body;
4713         // branched to use + in gecko and [].join() in others
4714         if(Roo.isGecko){
4715             body = "this.compiled = function(values){ return '" +
4716                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4717                     "';};";
4718         }else{
4719             body = ["this.compiled = function(values){ return ['"];
4720             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4721             body.push("'].join('');};");
4722             body = body.join('');
4723         }
4724         /**
4725          * eval:var:values
4726          * eval:var:fm
4727          */
4728         eval(body);
4729         return this;
4730     },
4731     
4732     // private function used to call members
4733     call : function(fnName, value, allValues){
4734         return this[fnName](value, allValues);
4735     },
4736     
4737     /**
4738      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4739      * @param {String/HTMLElement/Roo.Element} el The context element
4740      * @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'})
4741      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4742      * @return {HTMLElement/Roo.Element} The new node or Element
4743      */
4744     insertFirst: function(el, values, returnElement){
4745         return this.doInsert('afterBegin', el, values, returnElement);
4746     },
4747
4748     /**
4749      * Applies the supplied values to the template and inserts the new node(s) before el.
4750      * @param {String/HTMLElement/Roo.Element} el The context element
4751      * @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'})
4752      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4753      * @return {HTMLElement/Roo.Element} The new node or Element
4754      */
4755     insertBefore: function(el, values, returnElement){
4756         return this.doInsert('beforeBegin', el, values, returnElement);
4757     },
4758
4759     /**
4760      * Applies the supplied values to the template and inserts the new node(s) after el.
4761      * @param {String/HTMLElement/Roo.Element} el The context element
4762      * @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'})
4763      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4764      * @return {HTMLElement/Roo.Element} The new node or Element
4765      */
4766     insertAfter : function(el, values, returnElement){
4767         return this.doInsert('afterEnd', el, values, returnElement);
4768     },
4769     
4770     /**
4771      * Applies the supplied values to the template and appends the new node(s) to el.
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     append : function(el, values, returnElement){
4778         return this.doInsert('beforeEnd', el, values, returnElement);
4779     },
4780
4781     doInsert : function(where, el, values, returnEl){
4782         el = Roo.getDom(el);
4783         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4784         return returnEl ? Roo.get(newNode, true) : newNode;
4785     },
4786
4787     /**
4788      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4789      * @param {String/HTMLElement/Roo.Element} el The context element
4790      * @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'})
4791      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4792      * @return {HTMLElement/Roo.Element} The new node or Element
4793      */
4794     overwrite : function(el, values, returnElement){
4795         el = Roo.getDom(el);
4796         el.innerHTML = this.applyTemplate(values);
4797         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4798     }
4799 };
4800 /**
4801  * Alias for {@link #applyTemplate}
4802  * @method
4803  */
4804 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4805
4806 // backwards compat
4807 Roo.DomHelper.Template = Roo.Template;
4808
4809 /**
4810  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4811  * @param {String/HTMLElement} el A DOM element or its id
4812  * @returns {Roo.Template} The created template
4813  * @static
4814  */
4815 Roo.Template.from = function(el){
4816     el = Roo.getDom(el);
4817     return new Roo.Template(el.value || el.innerHTML);
4818 };/*
4819  * Based on:
4820  * Ext JS Library 1.1.1
4821  * Copyright(c) 2006-2007, Ext JS, LLC.
4822  *
4823  * Originally Released Under LGPL - original licence link has changed is not relivant.
4824  *
4825  * Fork - LGPL
4826  * <script type="text/javascript">
4827  */
4828  
4829
4830 /*
4831  * This is code is also distributed under MIT license for use
4832  * with jQuery and prototype JavaScript libraries.
4833  */
4834 /**
4835  * @class Roo.DomQuery
4836 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).
4837 <p>
4838 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>
4839
4840 <p>
4841 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.
4842 </p>
4843 <h4>Element Selectors:</h4>
4844 <ul class="list">
4845     <li> <b>*</b> any element</li>
4846     <li> <b>E</b> an element with the tag E</li>
4847     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4848     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4849     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4850     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4851 </ul>
4852 <h4>Attribute Selectors:</h4>
4853 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4854 <ul class="list">
4855     <li> <b>E[foo]</b> has an attribute "foo"</li>
4856     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4857     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4858     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4859     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4860     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4861     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4862 </ul>
4863 <h4>Pseudo Classes:</h4>
4864 <ul class="list">
4865     <li> <b>E:first-child</b> E is the first child of its parent</li>
4866     <li> <b>E:last-child</b> E is the last child of its parent</li>
4867     <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>
4868     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4869     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4870     <li> <b>E:only-child</b> E is the only child of its parent</li>
4871     <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>
4872     <li> <b>E:first</b> the first E in the resultset</li>
4873     <li> <b>E:last</b> the last E in the resultset</li>
4874     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4875     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4876     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4877     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4878     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4879     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4880     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4881     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4882     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4883 </ul>
4884 <h4>CSS Value Selectors:</h4>
4885 <ul class="list">
4886     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4887     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4888     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4889     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4890     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4891     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4892 </ul>
4893  * @singleton
4894  */
4895 Roo.DomQuery = function(){
4896     var cache = {}, simpleCache = {}, valueCache = {};
4897     var nonSpace = /\S/;
4898     var trimRe = /^\s+|\s+$/g;
4899     var tplRe = /\{(\d+)\}/g;
4900     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4901     var tagTokenRe = /^(#)?([\w-\*]+)/;
4902     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4903
4904     function child(p, index){
4905         var i = 0;
4906         var n = p.firstChild;
4907         while(n){
4908             if(n.nodeType == 1){
4909                if(++i == index){
4910                    return n;
4911                }
4912             }
4913             n = n.nextSibling;
4914         }
4915         return null;
4916     };
4917
4918     function next(n){
4919         while((n = n.nextSibling) && n.nodeType != 1);
4920         return n;
4921     };
4922
4923     function prev(n){
4924         while((n = n.previousSibling) && n.nodeType != 1);
4925         return n;
4926     };
4927
4928     function children(d){
4929         var n = d.firstChild, ni = -1;
4930             while(n){
4931                 var nx = n.nextSibling;
4932                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4933                     d.removeChild(n);
4934                 }else{
4935                     n.nodeIndex = ++ni;
4936                 }
4937                 n = nx;
4938             }
4939             return this;
4940         };
4941
4942     function byClassName(c, a, v){
4943         if(!v){
4944             return c;
4945         }
4946         var r = [], ri = -1, cn;
4947         for(var i = 0, ci; ci = c[i]; i++){
4948             if((' '+ci.className+' ').indexOf(v) != -1){
4949                 r[++ri] = ci;
4950             }
4951         }
4952         return r;
4953     };
4954
4955     function attrValue(n, attr){
4956         if(!n.tagName && typeof n.length != "undefined"){
4957             n = n[0];
4958         }
4959         if(!n){
4960             return null;
4961         }
4962         if(attr == "for"){
4963             return n.htmlFor;
4964         }
4965         if(attr == "class" || attr == "className"){
4966             return n.className;
4967         }
4968         return n.getAttribute(attr) || n[attr];
4969
4970     };
4971
4972     function getNodes(ns, mode, tagName){
4973         var result = [], ri = -1, cs;
4974         if(!ns){
4975             return result;
4976         }
4977         tagName = tagName || "*";
4978         if(typeof ns.getElementsByTagName != "undefined"){
4979             ns = [ns];
4980         }
4981         if(!mode){
4982             for(var i = 0, ni; ni = ns[i]; i++){
4983                 cs = ni.getElementsByTagName(tagName);
4984                 for(var j = 0, ci; ci = cs[j]; j++){
4985                     result[++ri] = ci;
4986                 }
4987             }
4988         }else if(mode == "/" || mode == ">"){
4989             var utag = tagName.toUpperCase();
4990             for(var i = 0, ni, cn; ni = ns[i]; i++){
4991                 cn = ni.children || ni.childNodes;
4992                 for(var j = 0, cj; cj = cn[j]; j++){
4993                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4994                         result[++ri] = cj;
4995                     }
4996                 }
4997             }
4998         }else if(mode == "+"){
4999             var utag = tagName.toUpperCase();
5000             for(var i = 0, n; n = ns[i]; i++){
5001                 while((n = n.nextSibling) && n.nodeType != 1);
5002                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5003                     result[++ri] = n;
5004                 }
5005             }
5006         }else if(mode == "~"){
5007             for(var i = 0, n; n = ns[i]; i++){
5008                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5009                 if(n){
5010                     result[++ri] = n;
5011                 }
5012             }
5013         }
5014         return result;
5015     };
5016
5017     function concat(a, b){
5018         if(b.slice){
5019             return a.concat(b);
5020         }
5021         for(var i = 0, l = b.length; i < l; i++){
5022             a[a.length] = b[i];
5023         }
5024         return a;
5025     }
5026
5027     function byTag(cs, tagName){
5028         if(cs.tagName || cs == document){
5029             cs = [cs];
5030         }
5031         if(!tagName){
5032             return cs;
5033         }
5034         var r = [], ri = -1;
5035         tagName = tagName.toLowerCase();
5036         for(var i = 0, ci; ci = cs[i]; i++){
5037             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5038                 r[++ri] = ci;
5039             }
5040         }
5041         return r;
5042     };
5043
5044     function byId(cs, attr, id){
5045         if(cs.tagName || cs == document){
5046             cs = [cs];
5047         }
5048         if(!id){
5049             return cs;
5050         }
5051         var r = [], ri = -1;
5052         for(var i = 0,ci; ci = cs[i]; i++){
5053             if(ci && ci.id == id){
5054                 r[++ri] = ci;
5055                 return r;
5056             }
5057         }
5058         return r;
5059     };
5060
5061     function byAttribute(cs, attr, value, op, custom){
5062         var r = [], ri = -1, st = custom=="{";
5063         var f = Roo.DomQuery.operators[op];
5064         for(var i = 0, ci; ci = cs[i]; i++){
5065             var a;
5066             if(st){
5067                 a = Roo.DomQuery.getStyle(ci, attr);
5068             }
5069             else if(attr == "class" || attr == "className"){
5070                 a = ci.className;
5071             }else if(attr == "for"){
5072                 a = ci.htmlFor;
5073             }else if(attr == "href"){
5074                 a = ci.getAttribute("href", 2);
5075             }else{
5076                 a = ci.getAttribute(attr);
5077             }
5078             if((f && f(a, value)) || (!f && a)){
5079                 r[++ri] = ci;
5080             }
5081         }
5082         return r;
5083     };
5084
5085     function byPseudo(cs, name, value){
5086         return Roo.DomQuery.pseudos[name](cs, value);
5087     };
5088
5089     // This is for IE MSXML which does not support expandos.
5090     // IE runs the same speed using setAttribute, however FF slows way down
5091     // and Safari completely fails so they need to continue to use expandos.
5092     var isIE = window.ActiveXObject ? true : false;
5093
5094     // this eval is stop the compressor from
5095     // renaming the variable to something shorter
5096     
5097     /** eval:var:batch */
5098     var batch = 30803; 
5099
5100     var key = 30803;
5101
5102     function nodupIEXml(cs){
5103         var d = ++key;
5104         cs[0].setAttribute("_nodup", d);
5105         var r = [cs[0]];
5106         for(var i = 1, len = cs.length; i < len; i++){
5107             var c = cs[i];
5108             if(!c.getAttribute("_nodup") != d){
5109                 c.setAttribute("_nodup", d);
5110                 r[r.length] = c;
5111             }
5112         }
5113         for(var i = 0, len = cs.length; i < len; i++){
5114             cs[i].removeAttribute("_nodup");
5115         }
5116         return r;
5117     }
5118
5119     function nodup(cs){
5120         if(!cs){
5121             return [];
5122         }
5123         var len = cs.length, c, i, r = cs, cj, ri = -1;
5124         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5125             return cs;
5126         }
5127         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5128             return nodupIEXml(cs);
5129         }
5130         var d = ++key;
5131         cs[0]._nodup = d;
5132         for(i = 1; c = cs[i]; i++){
5133             if(c._nodup != d){
5134                 c._nodup = d;
5135             }else{
5136                 r = [];
5137                 for(var j = 0; j < i; j++){
5138                     r[++ri] = cs[j];
5139                 }
5140                 for(j = i+1; cj = cs[j]; j++){
5141                     if(cj._nodup != d){
5142                         cj._nodup = d;
5143                         r[++ri] = cj;
5144                     }
5145                 }
5146                 return r;
5147             }
5148         }
5149         return r;
5150     }
5151
5152     function quickDiffIEXml(c1, c2){
5153         var d = ++key;
5154         for(var i = 0, len = c1.length; i < len; i++){
5155             c1[i].setAttribute("_qdiff", d);
5156         }
5157         var r = [];
5158         for(var i = 0, len = c2.length; i < len; i++){
5159             if(c2[i].getAttribute("_qdiff") != d){
5160                 r[r.length] = c2[i];
5161             }
5162         }
5163         for(var i = 0, len = c1.length; i < len; i++){
5164            c1[i].removeAttribute("_qdiff");
5165         }
5166         return r;
5167     }
5168
5169     function quickDiff(c1, c2){
5170         var len1 = c1.length;
5171         if(!len1){
5172             return c2;
5173         }
5174         if(isIE && c1[0].selectSingleNode){
5175             return quickDiffIEXml(c1, c2);
5176         }
5177         var d = ++key;
5178         for(var i = 0; i < len1; i++){
5179             c1[i]._qdiff = d;
5180         }
5181         var r = [];
5182         for(var i = 0, len = c2.length; i < len; i++){
5183             if(c2[i]._qdiff != d){
5184                 r[r.length] = c2[i];
5185             }
5186         }
5187         return r;
5188     }
5189
5190     function quickId(ns, mode, root, id){
5191         if(ns == root){
5192            var d = root.ownerDocument || root;
5193            return d.getElementById(id);
5194         }
5195         ns = getNodes(ns, mode, "*");
5196         return byId(ns, null, id);
5197     }
5198
5199     return {
5200         getStyle : function(el, name){
5201             return Roo.fly(el).getStyle(name);
5202         },
5203         /**
5204          * Compiles a selector/xpath query into a reusable function. The returned function
5205          * takes one parameter "root" (optional), which is the context node from where the query should start.
5206          * @param {String} selector The selector/xpath query
5207          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5208          * @return {Function}
5209          */
5210         compile : function(path, type){
5211             type = type || "select";
5212             
5213             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5214             var q = path, mode, lq;
5215             var tk = Roo.DomQuery.matchers;
5216             var tklen = tk.length;
5217             var mm;
5218
5219             // accept leading mode switch
5220             var lmode = q.match(modeRe);
5221             if(lmode && lmode[1]){
5222                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5223                 q = q.replace(lmode[1], "");
5224             }
5225             // strip leading slashes
5226             while(path.substr(0, 1)=="/"){
5227                 path = path.substr(1);
5228             }
5229
5230             while(q && lq != q){
5231                 lq = q;
5232                 var tm = q.match(tagTokenRe);
5233                 if(type == "select"){
5234                     if(tm){
5235                         if(tm[1] == "#"){
5236                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5237                         }else{
5238                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5239                         }
5240                         q = q.replace(tm[0], "");
5241                     }else if(q.substr(0, 1) != '@'){
5242                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5243                     }
5244                 }else{
5245                     if(tm){
5246                         if(tm[1] == "#"){
5247                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5248                         }else{
5249                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5250                         }
5251                         q = q.replace(tm[0], "");
5252                     }
5253                 }
5254                 while(!(mm = q.match(modeRe))){
5255                     var matched = false;
5256                     for(var j = 0; j < tklen; j++){
5257                         var t = tk[j];
5258                         var m = q.match(t.re);
5259                         if(m){
5260                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5261                                                     return m[i];
5262                                                 });
5263                             q = q.replace(m[0], "");
5264                             matched = true;
5265                             break;
5266                         }
5267                     }
5268                     // prevent infinite loop on bad selector
5269                     if(!matched){
5270                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5271                     }
5272                 }
5273                 if(mm[1]){
5274                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5275                     q = q.replace(mm[1], "");
5276                 }
5277             }
5278             fn[fn.length] = "return nodup(n);\n}";
5279             
5280              /** 
5281               * list of variables that need from compression as they are used by eval.
5282              *  eval:var:batch 
5283              *  eval:var:nodup
5284              *  eval:var:byTag
5285              *  eval:var:ById
5286              *  eval:var:getNodes
5287              *  eval:var:quickId
5288              *  eval:var:mode
5289              *  eval:var:root
5290              *  eval:var:n
5291              *  eval:var:byClassName
5292              *  eval:var:byPseudo
5293              *  eval:var:byAttribute
5294              *  eval:var:attrValue
5295              * 
5296              **/ 
5297             eval(fn.join(""));
5298             return f;
5299         },
5300
5301         /**
5302          * Selects a group of elements.
5303          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5304          * @param {Node} root (optional) The start of the query (defaults to document).
5305          * @return {Array}
5306          */
5307         select : function(path, root, type){
5308             if(!root || root == document){
5309                 root = document;
5310             }
5311             if(typeof root == "string"){
5312                 root = document.getElementById(root);
5313             }
5314             var paths = path.split(",");
5315             var results = [];
5316             for(var i = 0, len = paths.length; i < len; i++){
5317                 var p = paths[i].replace(trimRe, "");
5318                 if(!cache[p]){
5319                     cache[p] = Roo.DomQuery.compile(p);
5320                     if(!cache[p]){
5321                         throw p + " is not a valid selector";
5322                     }
5323                 }
5324                 var result = cache[p](root);
5325                 if(result && result != document){
5326                     results = results.concat(result);
5327                 }
5328             }
5329             if(paths.length > 1){
5330                 return nodup(results);
5331             }
5332             return results;
5333         },
5334
5335         /**
5336          * Selects a single element.
5337          * @param {String} selector The selector/xpath query
5338          * @param {Node} root (optional) The start of the query (defaults to document).
5339          * @return {Element}
5340          */
5341         selectNode : function(path, root){
5342             return Roo.DomQuery.select(path, root)[0];
5343         },
5344
5345         /**
5346          * Selects the value of a node, optionally replacing null with the defaultValue.
5347          * @param {String} selector The selector/xpath query
5348          * @param {Node} root (optional) The start of the query (defaults to document).
5349          * @param {String} defaultValue
5350          */
5351         selectValue : function(path, root, defaultValue){
5352             path = path.replace(trimRe, "");
5353             if(!valueCache[path]){
5354                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5355             }
5356             var n = valueCache[path](root);
5357             n = n[0] ? n[0] : n;
5358             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5359             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5360         },
5361
5362         /**
5363          * Selects the value of a node, parsing integers and floats.
5364          * @param {String} selector The selector/xpath query
5365          * @param {Node} root (optional) The start of the query (defaults to document).
5366          * @param {Number} defaultValue
5367          * @return {Number}
5368          */
5369         selectNumber : function(path, root, defaultValue){
5370             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5371             return parseFloat(v);
5372         },
5373
5374         /**
5375          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5376          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5377          * @param {String} selector The simple selector to test
5378          * @return {Boolean}
5379          */
5380         is : function(el, ss){
5381             if(typeof el == "string"){
5382                 el = document.getElementById(el);
5383             }
5384             var isArray = (el instanceof Array);
5385             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5386             return isArray ? (result.length == el.length) : (result.length > 0);
5387         },
5388
5389         /**
5390          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5391          * @param {Array} el An array of elements to filter
5392          * @param {String} selector The simple selector to test
5393          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5394          * the selector instead of the ones that match
5395          * @return {Array}
5396          */
5397         filter : function(els, ss, nonMatches){
5398             ss = ss.replace(trimRe, "");
5399             if(!simpleCache[ss]){
5400                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5401             }
5402             var result = simpleCache[ss](els);
5403             return nonMatches ? quickDiff(result, els) : result;
5404         },
5405
5406         /**
5407          * Collection of matching regular expressions and code snippets.
5408          */
5409         matchers : [{
5410                 re: /^\.([\w-]+)/,
5411                 select: 'n = byClassName(n, null, " {1} ");'
5412             }, {
5413                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5414                 select: 'n = byPseudo(n, "{1}", "{2}");'
5415             },{
5416                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5417                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5418             }, {
5419                 re: /^#([\w-]+)/,
5420                 select: 'n = byId(n, null, "{1}");'
5421             },{
5422                 re: /^@([\w-]+)/,
5423                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5424             }
5425         ],
5426
5427         /**
5428          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5429          * 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;.
5430          */
5431         operators : {
5432             "=" : function(a, v){
5433                 return a == v;
5434             },
5435             "!=" : function(a, v){
5436                 return a != v;
5437             },
5438             "^=" : function(a, v){
5439                 return a && a.substr(0, v.length) == v;
5440             },
5441             "$=" : function(a, v){
5442                 return a && a.substr(a.length-v.length) == v;
5443             },
5444             "*=" : function(a, v){
5445                 return a && a.indexOf(v) !== -1;
5446             },
5447             "%=" : function(a, v){
5448                 return (a % v) == 0;
5449             },
5450             "|=" : function(a, v){
5451                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5452             },
5453             "~=" : function(a, v){
5454                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5455             }
5456         },
5457
5458         /**
5459          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5460          * and the argument (if any) supplied in the selector.
5461          */
5462         pseudos : {
5463             "first-child" : function(c){
5464                 var r = [], ri = -1, n;
5465                 for(var i = 0, ci; ci = n = c[i]; i++){
5466                     while((n = n.previousSibling) && n.nodeType != 1);
5467                     if(!n){
5468                         r[++ri] = ci;
5469                     }
5470                 }
5471                 return r;
5472             },
5473
5474             "last-child" : function(c){
5475                 var r = [], ri = -1, n;
5476                 for(var i = 0, ci; ci = n = c[i]; i++){
5477                     while((n = n.nextSibling) && n.nodeType != 1);
5478                     if(!n){
5479                         r[++ri] = ci;
5480                     }
5481                 }
5482                 return r;
5483             },
5484
5485             "nth-child" : function(c, a) {
5486                 var r = [], ri = -1;
5487                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5488                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5489                 for(var i = 0, n; n = c[i]; i++){
5490                     var pn = n.parentNode;
5491                     if (batch != pn._batch) {
5492                         var j = 0;
5493                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5494                             if(cn.nodeType == 1){
5495                                cn.nodeIndex = ++j;
5496                             }
5497                         }
5498                         pn._batch = batch;
5499                     }
5500                     if (f == 1) {
5501                         if (l == 0 || n.nodeIndex == l){
5502                             r[++ri] = n;
5503                         }
5504                     } else if ((n.nodeIndex + l) % f == 0){
5505                         r[++ri] = n;
5506                     }
5507                 }
5508
5509                 return r;
5510             },
5511
5512             "only-child" : function(c){
5513                 var r = [], ri = -1;;
5514                 for(var i = 0, ci; ci = c[i]; i++){
5515                     if(!prev(ci) && !next(ci)){
5516                         r[++ri] = ci;
5517                     }
5518                 }
5519                 return r;
5520             },
5521
5522             "empty" : function(c){
5523                 var r = [], ri = -1;
5524                 for(var i = 0, ci; ci = c[i]; i++){
5525                     var cns = ci.childNodes, j = 0, cn, empty = true;
5526                     while(cn = cns[j]){
5527                         ++j;
5528                         if(cn.nodeType == 1 || cn.nodeType == 3){
5529                             empty = false;
5530                             break;
5531                         }
5532                     }
5533                     if(empty){
5534                         r[++ri] = ci;
5535                     }
5536                 }
5537                 return r;
5538             },
5539
5540             "contains" : function(c, v){
5541                 var r = [], ri = -1;
5542                 for(var i = 0, ci; ci = c[i]; i++){
5543                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5544                         r[++ri] = ci;
5545                     }
5546                 }
5547                 return r;
5548             },
5549
5550             "nodeValue" : function(c, v){
5551                 var r = [], ri = -1;
5552                 for(var i = 0, ci; ci = c[i]; i++){
5553                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5554                         r[++ri] = ci;
5555                     }
5556                 }
5557                 return r;
5558             },
5559
5560             "checked" : function(c){
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     if(ci.checked == true){
5564                         r[++ri] = ci;
5565                     }
5566                 }
5567                 return r;
5568             },
5569
5570             "not" : function(c, ss){
5571                 return Roo.DomQuery.filter(c, ss, true);
5572             },
5573
5574             "odd" : function(c){
5575                 return this["nth-child"](c, "odd");
5576             },
5577
5578             "even" : function(c){
5579                 return this["nth-child"](c, "even");
5580             },
5581
5582             "nth" : function(c, a){
5583                 return c[a-1] || [];
5584             },
5585
5586             "first" : function(c){
5587                 return c[0] || [];
5588             },
5589
5590             "last" : function(c){
5591                 return c[c.length-1] || [];
5592             },
5593
5594             "has" : function(c, ss){
5595                 var s = Roo.DomQuery.select;
5596                 var r = [], ri = -1;
5597                 for(var i = 0, ci; ci = c[i]; i++){
5598                     if(s(ss, ci).length > 0){
5599                         r[++ri] = ci;
5600                     }
5601                 }
5602                 return r;
5603             },
5604
5605             "next" : function(c, ss){
5606                 var is = Roo.DomQuery.is;
5607                 var r = [], ri = -1;
5608                 for(var i = 0, ci; ci = c[i]; i++){
5609                     var n = next(ci);
5610                     if(n && is(n, ss)){
5611                         r[++ri] = ci;
5612                     }
5613                 }
5614                 return r;
5615             },
5616
5617             "prev" : function(c, ss){
5618                 var is = Roo.DomQuery.is;
5619                 var r = [], ri = -1;
5620                 for(var i = 0, ci; ci = c[i]; i++){
5621                     var n = prev(ci);
5622                     if(n && is(n, ss)){
5623                         r[++ri] = ci;
5624                     }
5625                 }
5626                 return r;
5627             }
5628         }
5629     };
5630 }();
5631
5632 /**
5633  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5634  * @param {String} path The selector/xpath query
5635  * @param {Node} root (optional) The start of the query (defaults to document).
5636  * @return {Array}
5637  * @member Roo
5638  * @method query
5639  */
5640 Roo.query = Roo.DomQuery.select;
5641 /*
5642  * Based on:
5643  * Ext JS Library 1.1.1
5644  * Copyright(c) 2006-2007, Ext JS, LLC.
5645  *
5646  * Originally Released Under LGPL - original licence link has changed is not relivant.
5647  *
5648  * Fork - LGPL
5649  * <script type="text/javascript">
5650  */
5651
5652 /**
5653  * @class Roo.util.Observable
5654  * Base class that provides a common interface for publishing events. Subclasses are expected to
5655  * to have a property "events" with all the events defined.<br>
5656  * For example:
5657  * <pre><code>
5658  Employee = function(name){
5659     this.name = name;
5660     this.addEvents({
5661         "fired" : true,
5662         "quit" : true
5663     });
5664  }
5665  Roo.extend(Employee, Roo.util.Observable);
5666 </code></pre>
5667  * @param {Object} config properties to use (incuding events / listeners)
5668  */
5669
5670 Roo.util.Observable = function(cfg){
5671     
5672     cfg = cfg|| {};
5673     this.addEvents(cfg.events || {});
5674     if (cfg.events) {
5675         delete cfg.events; // make sure
5676     }
5677      
5678     Roo.apply(this, cfg);
5679     
5680     if(this.listeners){
5681         this.on(this.listeners);
5682         delete this.listeners;
5683     }
5684 };
5685 Roo.util.Observable.prototype = {
5686     /** 
5687  * @cfg {Object} listeners  list of events and functions to call for this object, 
5688  * For example :
5689  * <pre><code>
5690     listeners :  { 
5691        'click' : function(e) {
5692            ..... 
5693         } ,
5694         .... 
5695     } 
5696   </code></pre>
5697  */
5698     
5699     
5700     /**
5701      * Fires the specified event with the passed parameters (minus the event name).
5702      * @param {String} eventName
5703      * @param {Object...} args Variable number of parameters are passed to handlers
5704      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5705      */
5706     fireEvent : function(){
5707         var ce = this.events[arguments[0].toLowerCase()];
5708         if(typeof ce == "object"){
5709             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5710         }else{
5711             return true;
5712         }
5713     },
5714
5715     // private
5716     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5717
5718     /**
5719      * Appends an event handler to this component
5720      * @param {String}   eventName The type of event to listen for
5721      * @param {Function} handler The method the event invokes
5722      * @param {Object}   scope (optional) The scope in which to execute the handler
5723      * function. The handler function's "this" context.
5724      * @param {Object}   options (optional) An object containing handler configuration
5725      * properties. This may contain any of the following properties:<ul>
5726      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5727      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5728      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5729      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5730      * by the specified number of milliseconds. If the event fires again within that time, the original
5731      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5732      * </ul><br>
5733      * <p>
5734      * <b>Combining Options</b><br>
5735      * Using the options argument, it is possible to combine different types of listeners:<br>
5736      * <br>
5737      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5738                 <pre><code>
5739                 el.on('click', this.onClick, this, {
5740                         single: true,
5741                 delay: 100,
5742                 forumId: 4
5743                 });
5744                 </code></pre>
5745      * <p>
5746      * <b>Attaching multiple handlers in 1 call</b><br>
5747      * The method also allows for a single argument to be passed which is a config object containing properties
5748      * which specify multiple handlers.
5749      * <pre><code>
5750                 el.on({
5751                         'click': {
5752                         fn: this.onClick,
5753                         scope: this,
5754                         delay: 100
5755                 }, 
5756                 'mouseover': {
5757                         fn: this.onMouseOver,
5758                         scope: this
5759                 },
5760                 'mouseout': {
5761                         fn: this.onMouseOut,
5762                         scope: this
5763                 }
5764                 });
5765                 </code></pre>
5766      * <p>
5767      * Or a shorthand syntax which passes the same scope object to all handlers:
5768         <pre><code>
5769                 el.on({
5770                         'click': this.onClick,
5771                 'mouseover': this.onMouseOver,
5772                 'mouseout': this.onMouseOut,
5773                 scope: this
5774                 });
5775                 </code></pre>
5776      */
5777     addListener : function(eventName, fn, scope, o){
5778         if(typeof eventName == "object"){
5779             o = eventName;
5780             for(var e in o){
5781                 if(this.filterOptRe.test(e)){
5782                     continue;
5783                 }
5784                 if(typeof o[e] == "function"){
5785                     // shared options
5786                     this.addListener(e, o[e], o.scope,  o);
5787                 }else{
5788                     // individual options
5789                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5790                 }
5791             }
5792             return;
5793         }
5794         o = (!o || typeof o == "boolean") ? {} : o;
5795         eventName = eventName.toLowerCase();
5796         var ce = this.events[eventName] || true;
5797         if(typeof ce == "boolean"){
5798             ce = new Roo.util.Event(this, eventName);
5799             this.events[eventName] = ce;
5800         }
5801         ce.addListener(fn, scope, o);
5802     },
5803
5804     /**
5805      * Removes a listener
5806      * @param {String}   eventName     The type of event to listen for
5807      * @param {Function} handler        The handler to remove
5808      * @param {Object}   scope  (optional) The scope (this object) for the handler
5809      */
5810     removeListener : function(eventName, fn, scope){
5811         var ce = this.events[eventName.toLowerCase()];
5812         if(typeof ce == "object"){
5813             ce.removeListener(fn, scope);
5814         }
5815     },
5816
5817     /**
5818      * Removes all listeners for this object
5819      */
5820     purgeListeners : function(){
5821         for(var evt in this.events){
5822             if(typeof this.events[evt] == "object"){
5823                  this.events[evt].clearListeners();
5824             }
5825         }
5826     },
5827
5828     relayEvents : function(o, events){
5829         var createHandler = function(ename){
5830             return function(){
5831                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5832             };
5833         };
5834         for(var i = 0, len = events.length; i < len; i++){
5835             var ename = events[i];
5836             if(!this.events[ename]){ this.events[ename] = true; };
5837             o.on(ename, createHandler(ename), this);
5838         }
5839     },
5840
5841     /**
5842      * Used to define events on this Observable
5843      * @param {Object} object The object with the events defined
5844      */
5845     addEvents : function(o){
5846         if(!this.events){
5847             this.events = {};
5848         }
5849         Roo.applyIf(this.events, o);
5850     },
5851
5852     /**
5853      * Checks to see if this object has any listeners for a specified event
5854      * @param {String} eventName The name of the event to check for
5855      * @return {Boolean} True if the event is being listened for, else false
5856      */
5857     hasListener : function(eventName){
5858         var e = this.events[eventName];
5859         return typeof e == "object" && e.listeners.length > 0;
5860     }
5861 };
5862 /**
5863  * Appends an event handler to this element (shorthand for addListener)
5864  * @param {String}   eventName     The type of event to listen for
5865  * @param {Function} handler        The method the event invokes
5866  * @param {Object}   scope (optional) The scope in which to execute the handler
5867  * function. The handler function's "this" context.
5868  * @param {Object}   options  (optional)
5869  * @method
5870  */
5871 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5872 /**
5873  * Removes a listener (shorthand for removeListener)
5874  * @param {String}   eventName     The type of event to listen for
5875  * @param {Function} handler        The handler to remove
5876  * @param {Object}   scope  (optional) The scope (this object) for the handler
5877  * @method
5878  */
5879 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5880
5881 /**
5882  * Starts capture on the specified Observable. All events will be passed
5883  * to the supplied function with the event name + standard signature of the event
5884  * <b>before</b> the event is fired. If the supplied function returns false,
5885  * the event will not fire.
5886  * @param {Observable} o The Observable to capture
5887  * @param {Function} fn The function to call
5888  * @param {Object} scope (optional) The scope (this object) for the fn
5889  * @static
5890  */
5891 Roo.util.Observable.capture = function(o, fn, scope){
5892     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5893 };
5894
5895 /**
5896  * Removes <b>all</b> added captures from the Observable.
5897  * @param {Observable} o The Observable to release
5898  * @static
5899  */
5900 Roo.util.Observable.releaseCapture = function(o){
5901     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5902 };
5903
5904 (function(){
5905
5906     var createBuffered = function(h, o, scope){
5907         var task = new Roo.util.DelayedTask();
5908         return function(){
5909             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5910         };
5911     };
5912
5913     var createSingle = function(h, e, fn, scope){
5914         return function(){
5915             e.removeListener(fn, scope);
5916             return h.apply(scope, arguments);
5917         };
5918     };
5919
5920     var createDelayed = function(h, o, scope){
5921         return function(){
5922             var args = Array.prototype.slice.call(arguments, 0);
5923             setTimeout(function(){
5924                 h.apply(scope, args);
5925             }, o.delay || 10);
5926         };
5927     };
5928
5929     Roo.util.Event = function(obj, name){
5930         this.name = name;
5931         this.obj = obj;
5932         this.listeners = [];
5933     };
5934
5935     Roo.util.Event.prototype = {
5936         addListener : function(fn, scope, options){
5937             var o = options || {};
5938             scope = scope || this.obj;
5939             if(!this.isListening(fn, scope)){
5940                 var l = {fn: fn, scope: scope, options: o};
5941                 var h = fn;
5942                 if(o.delay){
5943                     h = createDelayed(h, o, scope);
5944                 }
5945                 if(o.single){
5946                     h = createSingle(h, this, fn, scope);
5947                 }
5948                 if(o.buffer){
5949                     h = createBuffered(h, o, scope);
5950                 }
5951                 l.fireFn = h;
5952                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5953                     this.listeners.push(l);
5954                 }else{
5955                     this.listeners = this.listeners.slice(0);
5956                     this.listeners.push(l);
5957                 }
5958             }
5959         },
5960
5961         findListener : function(fn, scope){
5962             scope = scope || this.obj;
5963             var ls = this.listeners;
5964             for(var i = 0, len = ls.length; i < len; i++){
5965                 var l = ls[i];
5966                 if(l.fn == fn && l.scope == scope){
5967                     return i;
5968                 }
5969             }
5970             return -1;
5971         },
5972
5973         isListening : function(fn, scope){
5974             return this.findListener(fn, scope) != -1;
5975         },
5976
5977         removeListener : function(fn, scope){
5978             var index;
5979             if((index = this.findListener(fn, scope)) != -1){
5980                 if(!this.firing){
5981                     this.listeners.splice(index, 1);
5982                 }else{
5983                     this.listeners = this.listeners.slice(0);
5984                     this.listeners.splice(index, 1);
5985                 }
5986                 return true;
5987             }
5988             return false;
5989         },
5990
5991         clearListeners : function(){
5992             this.listeners = [];
5993         },
5994
5995         fire : function(){
5996             var ls = this.listeners, scope, len = ls.length;
5997             if(len > 0){
5998                 this.firing = true;
5999                 var args = Array.prototype.slice.call(arguments, 0);
6000                 for(var i = 0; i < len; i++){
6001                     var l = ls[i];
6002                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6003                         this.firing = false;
6004                         return false;
6005                     }
6006                 }
6007                 this.firing = false;
6008             }
6009             return true;
6010         }
6011     };
6012 })();/*
6013  * Based on:
6014  * Ext JS Library 1.1.1
6015  * Copyright(c) 2006-2007, Ext JS, LLC.
6016  *
6017  * Originally Released Under LGPL - original licence link has changed is not relivant.
6018  *
6019  * Fork - LGPL
6020  * <script type="text/javascript">
6021  */
6022
6023 /**
6024  * @class Roo.EventManager
6025  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6026  * several useful events directly.
6027  * See {@link Roo.EventObject} for more details on normalized event objects.
6028  * @singleton
6029  */
6030 Roo.EventManager = function(){
6031     var docReadyEvent, docReadyProcId, docReadyState = false;
6032     var resizeEvent, resizeTask, textEvent, textSize;
6033     var E = Roo.lib.Event;
6034     var D = Roo.lib.Dom;
6035
6036
6037     var fireDocReady = function(){
6038         if(!docReadyState){
6039             docReadyState = true;
6040             Roo.isReady = true;
6041             if(docReadyProcId){
6042                 clearInterval(docReadyProcId);
6043             }
6044             if(Roo.isGecko || Roo.isOpera) {
6045                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6046             }
6047             if(Roo.isIE){
6048                 var defer = document.getElementById("ie-deferred-loader");
6049                 if(defer){
6050                     defer.onreadystatechange = null;
6051                     defer.parentNode.removeChild(defer);
6052                 }
6053             }
6054             if(docReadyEvent){
6055                 docReadyEvent.fire();
6056                 docReadyEvent.clearListeners();
6057             }
6058         }
6059     };
6060     
6061     var initDocReady = function(){
6062         docReadyEvent = new Roo.util.Event();
6063         if(Roo.isGecko || Roo.isOpera) {
6064             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6065         }else if(Roo.isIE){
6066             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6067             var defer = document.getElementById("ie-deferred-loader");
6068             defer.onreadystatechange = function(){
6069                 if(this.readyState == "complete"){
6070                     fireDocReady();
6071                 }
6072             };
6073         }else if(Roo.isSafari){ 
6074             docReadyProcId = setInterval(function(){
6075                 var rs = document.readyState;
6076                 if(rs == "complete") {
6077                     fireDocReady();     
6078                  }
6079             }, 10);
6080         }
6081         // no matter what, make sure it fires on load
6082         E.on(window, "load", fireDocReady);
6083     };
6084
6085     var createBuffered = function(h, o){
6086         var task = new Roo.util.DelayedTask(h);
6087         return function(e){
6088             // create new event object impl so new events don't wipe out properties
6089             e = new Roo.EventObjectImpl(e);
6090             task.delay(o.buffer, h, null, [e]);
6091         };
6092     };
6093
6094     var createSingle = function(h, el, ename, fn){
6095         return function(e){
6096             Roo.EventManager.removeListener(el, ename, fn);
6097             h(e);
6098         };
6099     };
6100
6101     var createDelayed = function(h, o){
6102         return function(e){
6103             // create new event object impl so new events don't wipe out properties
6104             e = new Roo.EventObjectImpl(e);
6105             setTimeout(function(){
6106                 h(e);
6107             }, o.delay || 10);
6108         };
6109     };
6110
6111     var listen = function(element, ename, opt, fn, scope){
6112         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6113         fn = fn || o.fn; scope = scope || o.scope;
6114         var el = Roo.getDom(element);
6115         if(!el){
6116             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6117         }
6118         var h = function(e){
6119             e = Roo.EventObject.setEvent(e);
6120             var t;
6121             if(o.delegate){
6122                 t = e.getTarget(o.delegate, el);
6123                 if(!t){
6124                     return;
6125                 }
6126             }else{
6127                 t = e.target;
6128             }
6129             if(o.stopEvent === true){
6130                 e.stopEvent();
6131             }
6132             if(o.preventDefault === true){
6133                e.preventDefault();
6134             }
6135             if(o.stopPropagation === true){
6136                 e.stopPropagation();
6137             }
6138
6139             if(o.normalized === false){
6140                 e = e.browserEvent;
6141             }
6142
6143             fn.call(scope || el, e, t, o);
6144         };
6145         if(o.delay){
6146             h = createDelayed(h, o);
6147         }
6148         if(o.single){
6149             h = createSingle(h, el, ename, fn);
6150         }
6151         if(o.buffer){
6152             h = createBuffered(h, o);
6153         }
6154         fn._handlers = fn._handlers || [];
6155         fn._handlers.push([Roo.id(el), ename, h]);
6156
6157         E.on(el, ename, h);
6158         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6159             el.addEventListener("DOMMouseScroll", h, false);
6160             E.on(window, 'unload', function(){
6161                 el.removeEventListener("DOMMouseScroll", h, false);
6162             });
6163         }
6164         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6165             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6166         }
6167         return h;
6168     };
6169
6170     var stopListening = function(el, ename, fn){
6171         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6172         if(hds){
6173             for(var i = 0, len = hds.length; i < len; i++){
6174                 var h = hds[i];
6175                 if(h[0] == id && h[1] == ename){
6176                     hd = h[2];
6177                     hds.splice(i, 1);
6178                     break;
6179                 }
6180             }
6181         }
6182         E.un(el, ename, hd);
6183         el = Roo.getDom(el);
6184         if(ename == "mousewheel" && el.addEventListener){
6185             el.removeEventListener("DOMMouseScroll", hd, false);
6186         }
6187         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6188             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6189         }
6190     };
6191
6192     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6193     
6194     var pub = {
6195         
6196         
6197         /** 
6198          * Fix for doc tools
6199          * @scope Roo.EventManager
6200          */
6201         
6202         
6203         /** 
6204          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6205          * object with a Roo.EventObject
6206          * @param {Function} fn        The method the event invokes
6207          * @param {Object}   scope    An object that becomes the scope of the handler
6208          * @param {boolean}  override If true, the obj passed in becomes
6209          *                             the execution scope of the listener
6210          * @return {Function} The wrapped function
6211          * @deprecated
6212          */
6213         wrap : function(fn, scope, override){
6214             return function(e){
6215                 Roo.EventObject.setEvent(e);
6216                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6217             };
6218         },
6219         
6220         /**
6221      * Appends an event handler to an element (shorthand for addListener)
6222      * @param {String/HTMLElement}   element        The html element or id to assign the
6223      * @param {String}   eventName The type of event to listen for
6224      * @param {Function} handler The method the event invokes
6225      * @param {Object}   scope (optional) The scope in which to execute the handler
6226      * function. The handler function's "this" context.
6227      * @param {Object}   options (optional) An object containing handler configuration
6228      * properties. This may contain any of the following properties:<ul>
6229      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6230      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6231      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6232      * <li>preventDefault {Boolean} True to prevent the default action</li>
6233      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6234      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6235      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6236      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6237      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6238      * by the specified number of milliseconds. If the event fires again within that time, the original
6239      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6240      * </ul><br>
6241      * <p>
6242      * <b>Combining Options</b><br>
6243      * Using the options argument, it is possible to combine different types of listeners:<br>
6244      * <br>
6245      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6246      * Code:<pre><code>
6247 el.on('click', this.onClick, this, {
6248     single: true,
6249     delay: 100,
6250     stopEvent : true,
6251     forumId: 4
6252 });</code></pre>
6253      * <p>
6254      * <b>Attaching multiple handlers in 1 call</b><br>
6255       * The method also allows for a single argument to be passed which is a config object containing properties
6256      * which specify multiple handlers.
6257      * <p>
6258      * Code:<pre><code>
6259 el.on({
6260     'click' : {
6261         fn: this.onClick
6262         scope: this,
6263         delay: 100
6264     },
6265     'mouseover' : {
6266         fn: this.onMouseOver
6267         scope: this
6268     },
6269     'mouseout' : {
6270         fn: this.onMouseOut
6271         scope: this
6272     }
6273 });</code></pre>
6274      * <p>
6275      * Or a shorthand syntax:<br>
6276      * Code:<pre><code>
6277 el.on({
6278     'click' : this.onClick,
6279     'mouseover' : this.onMouseOver,
6280     'mouseout' : this.onMouseOut
6281     scope: this
6282 });</code></pre>
6283      */
6284         addListener : function(element, eventName, fn, scope, options){
6285             if(typeof eventName == "object"){
6286                 var o = eventName;
6287                 for(var e in o){
6288                     if(propRe.test(e)){
6289                         continue;
6290                     }
6291                     if(typeof o[e] == "function"){
6292                         // shared options
6293                         listen(element, e, o, o[e], o.scope);
6294                     }else{
6295                         // individual options
6296                         listen(element, e, o[e]);
6297                     }
6298                 }
6299                 return;
6300             }
6301             return listen(element, eventName, options, fn, scope);
6302         },
6303         
6304         /**
6305          * Removes an event handler
6306          *
6307          * @param {String/HTMLElement}   element        The id or html element to remove the 
6308          *                             event from
6309          * @param {String}   eventName     The type of event
6310          * @param {Function} fn
6311          * @return {Boolean} True if a listener was actually removed
6312          */
6313         removeListener : function(element, eventName, fn){
6314             return stopListening(element, eventName, fn);
6315         },
6316         
6317         /**
6318          * Fires when the document is ready (before onload and before images are loaded). Can be 
6319          * accessed shorthanded Roo.onReady().
6320          * @param {Function} fn        The method the event invokes
6321          * @param {Object}   scope    An  object that becomes the scope of the handler
6322          * @param {boolean}  options
6323          */
6324         onDocumentReady : function(fn, scope, options){
6325             if(docReadyState){ // if it already fired
6326                 docReadyEvent.addListener(fn, scope, options);
6327                 docReadyEvent.fire();
6328                 docReadyEvent.clearListeners();
6329                 return;
6330             }
6331             if(!docReadyEvent){
6332                 initDocReady();
6333             }
6334             docReadyEvent.addListener(fn, scope, options);
6335         },
6336         
6337         /**
6338          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6339          * @param {Function} fn        The method the event invokes
6340          * @param {Object}   scope    An object that becomes the scope of the handler
6341          * @param {boolean}  options
6342          */
6343         onWindowResize : function(fn, scope, options){
6344             if(!resizeEvent){
6345                 resizeEvent = new Roo.util.Event();
6346                 resizeTask = new Roo.util.DelayedTask(function(){
6347                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6348                 });
6349                 E.on(window, "resize", function(){
6350                     if(Roo.isIE){
6351                         resizeTask.delay(50);
6352                     }else{
6353                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6354                     }
6355                 });
6356             }
6357             resizeEvent.addListener(fn, scope, options);
6358         },
6359
6360         /**
6361          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6362          * @param {Function} fn        The method the event invokes
6363          * @param {Object}   scope    An object that becomes the scope of the handler
6364          * @param {boolean}  options
6365          */
6366         onTextResize : function(fn, scope, options){
6367             if(!textEvent){
6368                 textEvent = new Roo.util.Event();
6369                 var textEl = new Roo.Element(document.createElement('div'));
6370                 textEl.dom.className = 'x-text-resize';
6371                 textEl.dom.innerHTML = 'X';
6372                 textEl.appendTo(document.body);
6373                 textSize = textEl.dom.offsetHeight;
6374                 setInterval(function(){
6375                     if(textEl.dom.offsetHeight != textSize){
6376                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6377                     }
6378                 }, this.textResizeInterval);
6379             }
6380             textEvent.addListener(fn, scope, options);
6381         },
6382
6383         /**
6384          * Removes the passed window resize listener.
6385          * @param {Function} fn        The method the event invokes
6386          * @param {Object}   scope    The scope of handler
6387          */
6388         removeResizeListener : function(fn, scope){
6389             if(resizeEvent){
6390                 resizeEvent.removeListener(fn, scope);
6391             }
6392         },
6393
6394         // private
6395         fireResize : function(){
6396             if(resizeEvent){
6397                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6398             }   
6399         },
6400         /**
6401          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6402          */
6403         ieDeferSrc : false,
6404         /**
6405          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6406          */
6407         textResizeInterval : 50
6408     };
6409     
6410     /**
6411      * Fix for doc tools
6412      * @scopeAlias pub=Roo.EventManager
6413      */
6414     
6415      /**
6416      * Appends an event handler to an element (shorthand for addListener)
6417      * @param {String/HTMLElement}   element        The html element or id to assign the
6418      * @param {String}   eventName The type of event to listen for
6419      * @param {Function} handler The method the event invokes
6420      * @param {Object}   scope (optional) The scope in which to execute the handler
6421      * function. The handler function's "this" context.
6422      * @param {Object}   options (optional) An object containing handler configuration
6423      * properties. This may contain any of the following properties:<ul>
6424      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6425      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6426      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6427      * <li>preventDefault {Boolean} True to prevent the default action</li>
6428      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6429      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6430      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6431      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6432      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6433      * by the specified number of milliseconds. If the event fires again within that time, the original
6434      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6435      * </ul><br>
6436      * <p>
6437      * <b>Combining Options</b><br>
6438      * Using the options argument, it is possible to combine different types of listeners:<br>
6439      * <br>
6440      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6441      * Code:<pre><code>
6442 el.on('click', this.onClick, this, {
6443     single: true,
6444     delay: 100,
6445     stopEvent : true,
6446     forumId: 4
6447 });</code></pre>
6448      * <p>
6449      * <b>Attaching multiple handlers in 1 call</b><br>
6450       * The method also allows for a single argument to be passed which is a config object containing properties
6451      * which specify multiple handlers.
6452      * <p>
6453      * Code:<pre><code>
6454 el.on({
6455     'click' : {
6456         fn: this.onClick
6457         scope: this,
6458         delay: 100
6459     },
6460     'mouseover' : {
6461         fn: this.onMouseOver
6462         scope: this
6463     },
6464     'mouseout' : {
6465         fn: this.onMouseOut
6466         scope: this
6467     }
6468 });</code></pre>
6469      * <p>
6470      * Or a shorthand syntax:<br>
6471      * Code:<pre><code>
6472 el.on({
6473     'click' : this.onClick,
6474     'mouseover' : this.onMouseOver,
6475     'mouseout' : this.onMouseOut
6476     scope: this
6477 });</code></pre>
6478      */
6479     pub.on = pub.addListener;
6480     pub.un = pub.removeListener;
6481
6482     pub.stoppedMouseDownEvent = new Roo.util.Event();
6483     return pub;
6484 }();
6485 /**
6486   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6487   * @param {Function} fn        The method the event invokes
6488   * @param {Object}   scope    An  object that becomes the scope of the handler
6489   * @param {boolean}  override If true, the obj passed in becomes
6490   *                             the execution scope of the listener
6491   * @member Roo
6492   * @method onReady
6493  */
6494 Roo.onReady = Roo.EventManager.onDocumentReady;
6495
6496 Roo.onReady(function(){
6497     var bd = Roo.get(document.body);
6498     if(!bd){ return; }
6499
6500     var cls = [
6501             Roo.isIE ? "roo-ie"
6502             : Roo.isGecko ? "roo-gecko"
6503             : Roo.isOpera ? "roo-opera"
6504             : Roo.isSafari ? "roo-safari" : ""];
6505
6506     if(Roo.isMac){
6507         cls.push("roo-mac");
6508     }
6509     if(Roo.isLinux){
6510         cls.push("roo-linux");
6511     }
6512     if(Roo.isBorderBox){
6513         cls.push('roo-border-box');
6514     }
6515     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6516         var p = bd.dom.parentNode;
6517         if(p){
6518             p.className += ' roo-strict';
6519         }
6520     }
6521     bd.addClass(cls.join(' '));
6522 });
6523
6524 /**
6525  * @class Roo.EventObject
6526  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6527  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6528  * Example:
6529  * <pre><code>
6530  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6531     e.preventDefault();
6532     var target = e.getTarget();
6533     ...
6534  }
6535  var myDiv = Roo.get("myDiv");
6536  myDiv.on("click", handleClick);
6537  //or
6538  Roo.EventManager.on("myDiv", 'click', handleClick);
6539  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6540  </code></pre>
6541  * @singleton
6542  */
6543 Roo.EventObject = function(){
6544     
6545     var E = Roo.lib.Event;
6546     
6547     // safari keypress events for special keys return bad keycodes
6548     var safariKeys = {
6549         63234 : 37, // left
6550         63235 : 39, // right
6551         63232 : 38, // up
6552         63233 : 40, // down
6553         63276 : 33, // page up
6554         63277 : 34, // page down
6555         63272 : 46, // delete
6556         63273 : 36, // home
6557         63275 : 35  // end
6558     };
6559
6560     // normalize button clicks
6561     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6562                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6563
6564     Roo.EventObjectImpl = function(e){
6565         if(e){
6566             this.setEvent(e.browserEvent || e);
6567         }
6568     };
6569     Roo.EventObjectImpl.prototype = {
6570         /**
6571          * Used to fix doc tools.
6572          * @scope Roo.EventObject.prototype
6573          */
6574             
6575
6576         
6577         
6578         /** The normal browser event */
6579         browserEvent : null,
6580         /** The button pressed in a mouse event */
6581         button : -1,
6582         /** True if the shift key was down during the event */
6583         shiftKey : false,
6584         /** True if the control key was down during the event */
6585         ctrlKey : false,
6586         /** True if the alt key was down during the event */
6587         altKey : false,
6588
6589         /** Key constant 
6590         * @type Number */
6591         BACKSPACE : 8,
6592         /** Key constant 
6593         * @type Number */
6594         TAB : 9,
6595         /** Key constant 
6596         * @type Number */
6597         RETURN : 13,
6598         /** Key constant 
6599         * @type Number */
6600         ENTER : 13,
6601         /** Key constant 
6602         * @type Number */
6603         SHIFT : 16,
6604         /** Key constant 
6605         * @type Number */
6606         CONTROL : 17,
6607         /** Key constant 
6608         * @type Number */
6609         ESC : 27,
6610         /** Key constant 
6611         * @type Number */
6612         SPACE : 32,
6613         /** Key constant 
6614         * @type Number */
6615         PAGEUP : 33,
6616         /** Key constant 
6617         * @type Number */
6618         PAGEDOWN : 34,
6619         /** Key constant 
6620         * @type Number */
6621         END : 35,
6622         /** Key constant 
6623         * @type Number */
6624         HOME : 36,
6625         /** Key constant 
6626         * @type Number */
6627         LEFT : 37,
6628         /** Key constant 
6629         * @type Number */
6630         UP : 38,
6631         /** Key constant 
6632         * @type Number */
6633         RIGHT : 39,
6634         /** Key constant 
6635         * @type Number */
6636         DOWN : 40,
6637         /** Key constant 
6638         * @type Number */
6639         DELETE : 46,
6640         /** Key constant 
6641         * @type Number */
6642         F5 : 116,
6643
6644            /** @private */
6645         setEvent : function(e){
6646             if(e == this || (e && e.browserEvent)){ // already wrapped
6647                 return e;
6648             }
6649             this.browserEvent = e;
6650             if(e){
6651                 // normalize buttons
6652                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6653                 if(e.type == 'click' && this.button == -1){
6654                     this.button = 0;
6655                 }
6656                 this.type = e.type;
6657                 this.shiftKey = e.shiftKey;
6658                 // mac metaKey behaves like ctrlKey
6659                 this.ctrlKey = e.ctrlKey || e.metaKey;
6660                 this.altKey = e.altKey;
6661                 // in getKey these will be normalized for the mac
6662                 this.keyCode = e.keyCode;
6663                 // keyup warnings on firefox.
6664                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6665                 // cache the target for the delayed and or buffered events
6666                 this.target = E.getTarget(e);
6667                 // same for XY
6668                 this.xy = E.getXY(e);
6669             }else{
6670                 this.button = -1;
6671                 this.shiftKey = false;
6672                 this.ctrlKey = false;
6673                 this.altKey = false;
6674                 this.keyCode = 0;
6675                 this.charCode =0;
6676                 this.target = null;
6677                 this.xy = [0, 0];
6678             }
6679             return this;
6680         },
6681
6682         /**
6683          * Stop the event (preventDefault and stopPropagation)
6684          */
6685         stopEvent : function(){
6686             if(this.browserEvent){
6687                 if(this.browserEvent.type == 'mousedown'){
6688                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6689                 }
6690                 E.stopEvent(this.browserEvent);
6691             }
6692         },
6693
6694         /**
6695          * Prevents the browsers default handling of the event.
6696          */
6697         preventDefault : function(){
6698             if(this.browserEvent){
6699                 E.preventDefault(this.browserEvent);
6700             }
6701         },
6702
6703         /** @private */
6704         isNavKeyPress : function(){
6705             var k = this.keyCode;
6706             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6707             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6708         },
6709
6710         isSpecialKey : function(){
6711             var k = this.keyCode;
6712             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6713             (k == 16) || (k == 17) ||
6714             (k >= 18 && k <= 20) ||
6715             (k >= 33 && k <= 35) ||
6716             (k >= 36 && k <= 39) ||
6717             (k >= 44 && k <= 45);
6718         },
6719         /**
6720          * Cancels bubbling of the event.
6721          */
6722         stopPropagation : function(){
6723             if(this.browserEvent){
6724                 if(this.type == 'mousedown'){
6725                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6726                 }
6727                 E.stopPropagation(this.browserEvent);
6728             }
6729         },
6730
6731         /**
6732          * Gets the key code for the event.
6733          * @return {Number}
6734          */
6735         getCharCode : function(){
6736             return this.charCode || this.keyCode;
6737         },
6738
6739         /**
6740          * Returns a normalized keyCode for the event.
6741          * @return {Number} The key code
6742          */
6743         getKey : function(){
6744             var k = this.keyCode || this.charCode;
6745             return Roo.isSafari ? (safariKeys[k] || k) : k;
6746         },
6747
6748         /**
6749          * Gets the x coordinate of the event.
6750          * @return {Number}
6751          */
6752         getPageX : function(){
6753             return this.xy[0];
6754         },
6755
6756         /**
6757          * Gets the y coordinate of the event.
6758          * @return {Number}
6759          */
6760         getPageY : function(){
6761             return this.xy[1];
6762         },
6763
6764         /**
6765          * Gets the time of the event.
6766          * @return {Number}
6767          */
6768         getTime : function(){
6769             if(this.browserEvent){
6770                 return E.getTime(this.browserEvent);
6771             }
6772             return null;
6773         },
6774
6775         /**
6776          * Gets the page coordinates of the event.
6777          * @return {Array} The xy values like [x, y]
6778          */
6779         getXY : function(){
6780             return this.xy;
6781         },
6782
6783         /**
6784          * Gets the target for the event.
6785          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6786          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6787                 search as a number or element (defaults to 10 || document.body)
6788          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6789          * @return {HTMLelement}
6790          */
6791         getTarget : function(selector, maxDepth, returnEl){
6792             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6793         },
6794         /**
6795          * Gets the related target.
6796          * @return {HTMLElement}
6797          */
6798         getRelatedTarget : function(){
6799             if(this.browserEvent){
6800                 return E.getRelatedTarget(this.browserEvent);
6801             }
6802             return null;
6803         },
6804
6805         /**
6806          * Normalizes mouse wheel delta across browsers
6807          * @return {Number} The delta
6808          */
6809         getWheelDelta : function(){
6810             var e = this.browserEvent;
6811             var delta = 0;
6812             if(e.wheelDelta){ /* IE/Opera. */
6813                 delta = e.wheelDelta/120;
6814             }else if(e.detail){ /* Mozilla case. */
6815                 delta = -e.detail/3;
6816             }
6817             return delta;
6818         },
6819
6820         /**
6821          * Returns true if the control, meta, shift or alt key was pressed during this event.
6822          * @return {Boolean}
6823          */
6824         hasModifier : function(){
6825             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6826         },
6827
6828         /**
6829          * Returns true if the target of this event equals el or is a child of el
6830          * @param {String/HTMLElement/Element} el
6831          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6832          * @return {Boolean}
6833          */
6834         within : function(el, related){
6835             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6836             return t && Roo.fly(el).contains(t);
6837         },
6838
6839         getPoint : function(){
6840             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6841         }
6842     };
6843
6844     return new Roo.EventObjectImpl();
6845 }();
6846             
6847     /*
6848  * Based on:
6849  * Ext JS Library 1.1.1
6850  * Copyright(c) 2006-2007, Ext JS, LLC.
6851  *
6852  * Originally Released Under LGPL - original licence link has changed is not relivant.
6853  *
6854  * Fork - LGPL
6855  * <script type="text/javascript">
6856  */
6857
6858  
6859 // was in Composite Element!??!?!
6860  
6861 (function(){
6862     var D = Roo.lib.Dom;
6863     var E = Roo.lib.Event;
6864     var A = Roo.lib.Anim;
6865
6866     // local style camelizing for speed
6867     var propCache = {};
6868     var camelRe = /(-[a-z])/gi;
6869     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6870     var view = document.defaultView;
6871
6872 /**
6873  * @class Roo.Element
6874  * Represents an Element in the DOM.<br><br>
6875  * Usage:<br>
6876 <pre><code>
6877 var el = Roo.get("my-div");
6878
6879 // or with getEl
6880 var el = getEl("my-div");
6881
6882 // or with a DOM element
6883 var el = Roo.get(myDivElement);
6884 </code></pre>
6885  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6886  * each call instead of constructing a new one.<br><br>
6887  * <b>Animations</b><br />
6888  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6889  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6890 <pre>
6891 Option    Default   Description
6892 --------- --------  ---------------------------------------------
6893 duration  .35       The duration of the animation in seconds
6894 easing    easeOut   The YUI easing method
6895 callback  none      A function to execute when the anim completes
6896 scope     this      The scope (this) of the callback function
6897 </pre>
6898 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6899 * manipulate the animation. Here's an example:
6900 <pre><code>
6901 var el = Roo.get("my-div");
6902
6903 // no animation
6904 el.setWidth(100);
6905
6906 // default animation
6907 el.setWidth(100, true);
6908
6909 // animation with some options set
6910 el.setWidth(100, {
6911     duration: 1,
6912     callback: this.foo,
6913     scope: this
6914 });
6915
6916 // using the "anim" property to get the Anim object
6917 var opt = {
6918     duration: 1,
6919     callback: this.foo,
6920     scope: this
6921 };
6922 el.setWidth(100, opt);
6923 ...
6924 if(opt.anim.isAnimated()){
6925     opt.anim.stop();
6926 }
6927 </code></pre>
6928 * <b> Composite (Collections of) Elements</b><br />
6929  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6930  * @constructor Create a new Element directly.
6931  * @param {String/HTMLElement} element
6932  * @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).
6933  */
6934     Roo.Element = function(element, forceNew){
6935         var dom = typeof element == "string" ?
6936                 document.getElementById(element) : element;
6937         if(!dom){ // invalid id/element
6938             return null;
6939         }
6940         var id = dom.id;
6941         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6942             return Roo.Element.cache[id];
6943         }
6944
6945         /**
6946          * The DOM element
6947          * @type HTMLElement
6948          */
6949         this.dom = dom;
6950
6951         /**
6952          * The DOM element ID
6953          * @type String
6954          */
6955         this.id = id || Roo.id(dom);
6956     };
6957
6958     var El = Roo.Element;
6959
6960     El.prototype = {
6961         /**
6962          * The element's default display mode  (defaults to "")
6963          * @type String
6964          */
6965         originalDisplay : "",
6966
6967         visibilityMode : 1,
6968         /**
6969          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6970          * @type String
6971          */
6972         defaultUnit : "px",
6973         /**
6974          * Sets the element's visibility mode. When setVisible() is called it
6975          * will use this to determine whether to set the visibility or the display property.
6976          * @param visMode Element.VISIBILITY or Element.DISPLAY
6977          * @return {Roo.Element} this
6978          */
6979         setVisibilityMode : function(visMode){
6980             this.visibilityMode = visMode;
6981             return this;
6982         },
6983         /**
6984          * Convenience method for setVisibilityMode(Element.DISPLAY)
6985          * @param {String} display (optional) What to set display to when visible
6986          * @return {Roo.Element} this
6987          */
6988         enableDisplayMode : function(display){
6989             this.setVisibilityMode(El.DISPLAY);
6990             if(typeof display != "undefined") this.originalDisplay = display;
6991             return this;
6992         },
6993
6994         /**
6995          * 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)
6996          * @param {String} selector The simple selector to test
6997          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6998                 search as a number or element (defaults to 10 || document.body)
6999          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7000          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7001          */
7002         findParent : function(simpleSelector, maxDepth, returnEl){
7003             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7004             maxDepth = maxDepth || 50;
7005             if(typeof maxDepth != "number"){
7006                 stopEl = Roo.getDom(maxDepth);
7007                 maxDepth = 10;
7008             }
7009             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7010                 if(dq.is(p, simpleSelector)){
7011                     return returnEl ? Roo.get(p) : p;
7012                 }
7013                 depth++;
7014                 p = p.parentNode;
7015             }
7016             return null;
7017         },
7018
7019
7020         /**
7021          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7022          * @param {String} selector The simple selector to test
7023          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7024                 search as a number or element (defaults to 10 || document.body)
7025          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7026          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7027          */
7028         findParentNode : function(simpleSelector, maxDepth, returnEl){
7029             var p = Roo.fly(this.dom.parentNode, '_internal');
7030             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7031         },
7032
7033         /**
7034          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7035          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7036          * @param {String} selector The simple selector to test
7037          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7038                 search as a number or element (defaults to 10 || document.body)
7039          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7040          */
7041         up : function(simpleSelector, maxDepth){
7042             return this.findParentNode(simpleSelector, maxDepth, true);
7043         },
7044
7045
7046
7047         /**
7048          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7049          * @param {String} selector The simple selector to test
7050          * @return {Boolean} True if this element matches the selector, else false
7051          */
7052         is : function(simpleSelector){
7053             return Roo.DomQuery.is(this.dom, simpleSelector);
7054         },
7055
7056         /**
7057          * Perform animation on this element.
7058          * @param {Object} args The YUI animation control args
7059          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7060          * @param {Function} onComplete (optional) Function to call when animation completes
7061          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7062          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7063          * @return {Roo.Element} this
7064          */
7065         animate : function(args, duration, onComplete, easing, animType){
7066             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7067             return this;
7068         },
7069
7070         /*
7071          * @private Internal animation call
7072          */
7073         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7074             animType = animType || 'run';
7075             opt = opt || {};
7076             var anim = Roo.lib.Anim[animType](
7077                 this.dom, args,
7078                 (opt.duration || defaultDur) || .35,
7079                 (opt.easing || defaultEase) || 'easeOut',
7080                 function(){
7081                     Roo.callback(cb, this);
7082                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7083                 },
7084                 this
7085             );
7086             opt.anim = anim;
7087             return anim;
7088         },
7089
7090         // private legacy anim prep
7091         preanim : function(a, i){
7092             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7093         },
7094
7095         /**
7096          * Removes worthless text nodes
7097          * @param {Boolean} forceReclean (optional) By default the element
7098          * keeps track if it has been cleaned already so
7099          * you can call this over and over. However, if you update the element and
7100          * need to force a reclean, you can pass true.
7101          */
7102         clean : function(forceReclean){
7103             if(this.isCleaned && forceReclean !== true){
7104                 return this;
7105             }
7106             var ns = /\S/;
7107             var d = this.dom, n = d.firstChild, ni = -1;
7108             while(n){
7109                 var nx = n.nextSibling;
7110                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7111                     d.removeChild(n);
7112                 }else{
7113                     n.nodeIndex = ++ni;
7114                 }
7115                 n = nx;
7116             }
7117             this.isCleaned = true;
7118             return this;
7119         },
7120
7121         // private
7122         calcOffsetsTo : function(el){
7123             el = Roo.get(el);
7124             var d = el.dom;
7125             var restorePos = false;
7126             if(el.getStyle('position') == 'static'){
7127                 el.position('relative');
7128                 restorePos = true;
7129             }
7130             var x = 0, y =0;
7131             var op = this.dom;
7132             while(op && op != d && op.tagName != 'HTML'){
7133                 x+= op.offsetLeft;
7134                 y+= op.offsetTop;
7135                 op = op.offsetParent;
7136             }
7137             if(restorePos){
7138                 el.position('static');
7139             }
7140             return [x, y];
7141         },
7142
7143         /**
7144          * Scrolls this element into view within the passed container.
7145          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7146          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7147          * @return {Roo.Element} this
7148          */
7149         scrollIntoView : function(container, hscroll){
7150             var c = Roo.getDom(container) || document.body;
7151             var el = this.dom;
7152
7153             var o = this.calcOffsetsTo(c),
7154                 l = o[0],
7155                 t = o[1],
7156                 b = t+el.offsetHeight,
7157                 r = l+el.offsetWidth;
7158
7159             var ch = c.clientHeight;
7160             var ct = parseInt(c.scrollTop, 10);
7161             var cl = parseInt(c.scrollLeft, 10);
7162             var cb = ct + ch;
7163             var cr = cl + c.clientWidth;
7164
7165             if(t < ct){
7166                 c.scrollTop = t;
7167             }else if(b > cb){
7168                 c.scrollTop = b-ch;
7169             }
7170
7171             if(hscroll !== false){
7172                 if(l < cl){
7173                     c.scrollLeft = l;
7174                 }else if(r > cr){
7175                     c.scrollLeft = r-c.clientWidth;
7176                 }
7177             }
7178             return this;
7179         },
7180
7181         // private
7182         scrollChildIntoView : function(child, hscroll){
7183             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7184         },
7185
7186         /**
7187          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7188          * the new height may not be available immediately.
7189          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7190          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7191          * @param {Function} onComplete (optional) Function to call when animation completes
7192          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7193          * @return {Roo.Element} this
7194          */
7195         autoHeight : function(animate, duration, onComplete, easing){
7196             var oldHeight = this.getHeight();
7197             this.clip();
7198             this.setHeight(1); // force clipping
7199             setTimeout(function(){
7200                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7201                 if(!animate){
7202                     this.setHeight(height);
7203                     this.unclip();
7204                     if(typeof onComplete == "function"){
7205                         onComplete();
7206                     }
7207                 }else{
7208                     this.setHeight(oldHeight); // restore original height
7209                     this.setHeight(height, animate, duration, function(){
7210                         this.unclip();
7211                         if(typeof onComplete == "function") onComplete();
7212                     }.createDelegate(this), easing);
7213                 }
7214             }.createDelegate(this), 0);
7215             return this;
7216         },
7217
7218         /**
7219          * Returns true if this element is an ancestor of the passed element
7220          * @param {HTMLElement/String} el The element to check
7221          * @return {Boolean} True if this element is an ancestor of el, else false
7222          */
7223         contains : function(el){
7224             if(!el){return false;}
7225             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7226         },
7227
7228         /**
7229          * Checks whether the element is currently visible using both visibility and display properties.
7230          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7231          * @return {Boolean} True if the element is currently visible, else false
7232          */
7233         isVisible : function(deep) {
7234             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7235             if(deep !== true || !vis){
7236                 return vis;
7237             }
7238             var p = this.dom.parentNode;
7239             while(p && p.tagName.toLowerCase() != "body"){
7240                 if(!Roo.fly(p, '_isVisible').isVisible()){
7241                     return false;
7242                 }
7243                 p = p.parentNode;
7244             }
7245             return true;
7246         },
7247
7248         /**
7249          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7250          * @param {String} selector The CSS selector
7251          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7252          * @return {CompositeElement/CompositeElementLite} The composite element
7253          */
7254         select : function(selector, unique){
7255             return El.select(selector, unique, this.dom);
7256         },
7257
7258         /**
7259          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7260          * @param {String} selector The CSS selector
7261          * @return {Array} An array of the matched nodes
7262          */
7263         query : function(selector, unique){
7264             return Roo.DomQuery.select(selector, this.dom);
7265         },
7266
7267         /**
7268          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7269          * @param {String} selector The CSS selector
7270          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7271          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7272          */
7273         child : function(selector, returnDom){
7274             var n = Roo.DomQuery.selectNode(selector, this.dom);
7275             return returnDom ? n : Roo.get(n);
7276         },
7277
7278         /**
7279          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7280          * @param {String} selector The CSS selector
7281          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7282          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7283          */
7284         down : function(selector, returnDom){
7285             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7286             return returnDom ? n : Roo.get(n);
7287         },
7288
7289         /**
7290          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7291          * @param {String} group The group the DD object is member of
7292          * @param {Object} config The DD config object
7293          * @param {Object} overrides An object containing methods to override/implement on the DD object
7294          * @return {Roo.dd.DD} The DD object
7295          */
7296         initDD : function(group, config, overrides){
7297             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7298             return Roo.apply(dd, overrides);
7299         },
7300
7301         /**
7302          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7303          * @param {String} group The group the DDProxy object is member of
7304          * @param {Object} config The DDProxy config object
7305          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7306          * @return {Roo.dd.DDProxy} The DDProxy object
7307          */
7308         initDDProxy : function(group, config, overrides){
7309             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7310             return Roo.apply(dd, overrides);
7311         },
7312
7313         /**
7314          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7315          * @param {String} group The group the DDTarget object is member of
7316          * @param {Object} config The DDTarget config object
7317          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7318          * @return {Roo.dd.DDTarget} The DDTarget object
7319          */
7320         initDDTarget : function(group, config, overrides){
7321             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7322             return Roo.apply(dd, overrides);
7323         },
7324
7325         /**
7326          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7327          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7328          * @param {Boolean} visible Whether the element is visible
7329          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7330          * @return {Roo.Element} this
7331          */
7332          setVisible : function(visible, animate){
7333             if(!animate || !A){
7334                 if(this.visibilityMode == El.DISPLAY){
7335                     this.setDisplayed(visible);
7336                 }else{
7337                     this.fixDisplay();
7338                     this.dom.style.visibility = visible ? "visible" : "hidden";
7339                 }
7340             }else{
7341                 // closure for composites
7342                 var dom = this.dom;
7343                 var visMode = this.visibilityMode;
7344                 if(visible){
7345                     this.setOpacity(.01);
7346                     this.setVisible(true);
7347                 }
7348                 this.anim({opacity: { to: (visible?1:0) }},
7349                       this.preanim(arguments, 1),
7350                       null, .35, 'easeIn', function(){
7351                          if(!visible){
7352                              if(visMode == El.DISPLAY){
7353                                  dom.style.display = "none";
7354                              }else{
7355                                  dom.style.visibility = "hidden";
7356                              }
7357                              Roo.get(dom).setOpacity(1);
7358                          }
7359                      });
7360             }
7361             return this;
7362         },
7363
7364         /**
7365          * Returns true if display is not "none"
7366          * @return {Boolean}
7367          */
7368         isDisplayed : function() {
7369             return this.getStyle("display") != "none";
7370         },
7371
7372         /**
7373          * Toggles the element's visibility or display, depending on visibility mode.
7374          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7375          * @return {Roo.Element} this
7376          */
7377         toggle : function(animate){
7378             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7379             return this;
7380         },
7381
7382         /**
7383          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7384          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7385          * @return {Roo.Element} this
7386          */
7387         setDisplayed : function(value) {
7388             if(typeof value == "boolean"){
7389                value = value ? this.originalDisplay : "none";
7390             }
7391             this.setStyle("display", value);
7392             return this;
7393         },
7394
7395         /**
7396          * Tries to focus the element. Any exceptions are caught and ignored.
7397          * @return {Roo.Element} this
7398          */
7399         focus : function() {
7400             try{
7401                 this.dom.focus();
7402             }catch(e){}
7403             return this;
7404         },
7405
7406         /**
7407          * Tries to blur the element. Any exceptions are caught and ignored.
7408          * @return {Roo.Element} this
7409          */
7410         blur : function() {
7411             try{
7412                 this.dom.blur();
7413             }catch(e){}
7414             return this;
7415         },
7416
7417         /**
7418          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7419          * @param {String/Array} className The CSS class to add, or an array of classes
7420          * @return {Roo.Element} this
7421          */
7422         addClass : function(className){
7423             if(className instanceof Array){
7424                 for(var i = 0, len = className.length; i < len; i++) {
7425                     this.addClass(className[i]);
7426                 }
7427             }else{
7428                 if(className && !this.hasClass(className)){
7429                     this.dom.className = this.dom.className + " " + className;
7430                 }
7431             }
7432             return this;
7433         },
7434
7435         /**
7436          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7437          * @param {String/Array} className The CSS class to add, or an array of classes
7438          * @return {Roo.Element} this
7439          */
7440         radioClass : function(className){
7441             var siblings = this.dom.parentNode.childNodes;
7442             for(var i = 0; i < siblings.length; i++) {
7443                 var s = siblings[i];
7444                 if(s.nodeType == 1){
7445                     Roo.get(s).removeClass(className);
7446                 }
7447             }
7448             this.addClass(className);
7449             return this;
7450         },
7451
7452         /**
7453          * Removes one or more CSS classes from the element.
7454          * @param {String/Array} className The CSS class to remove, or an array of classes
7455          * @return {Roo.Element} this
7456          */
7457         removeClass : function(className){
7458             if(!className || !this.dom.className){
7459                 return this;
7460             }
7461             if(className instanceof Array){
7462                 for(var i = 0, len = className.length; i < len; i++) {
7463                     this.removeClass(className[i]);
7464                 }
7465             }else{
7466                 if(this.hasClass(className)){
7467                     var re = this.classReCache[className];
7468                     if (!re) {
7469                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7470                        this.classReCache[className] = re;
7471                     }
7472                     this.dom.className =
7473                         this.dom.className.replace(re, " ");
7474                 }
7475             }
7476             return this;
7477         },
7478
7479         // private
7480         classReCache: {},
7481
7482         /**
7483          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7484          * @param {String} className The CSS class to toggle
7485          * @return {Roo.Element} this
7486          */
7487         toggleClass : function(className){
7488             if(this.hasClass(className)){
7489                 this.removeClass(className);
7490             }else{
7491                 this.addClass(className);
7492             }
7493             return this;
7494         },
7495
7496         /**
7497          * Checks if the specified CSS class exists on this element's DOM node.
7498          * @param {String} className The CSS class to check for
7499          * @return {Boolean} True if the class exists, else false
7500          */
7501         hasClass : function(className){
7502             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7503         },
7504
7505         /**
7506          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7507          * @param {String} oldClassName The CSS class to replace
7508          * @param {String} newClassName The replacement CSS class
7509          * @return {Roo.Element} this
7510          */
7511         replaceClass : function(oldClassName, newClassName){
7512             this.removeClass(oldClassName);
7513             this.addClass(newClassName);
7514             return this;
7515         },
7516
7517         /**
7518          * Returns an object with properties matching the styles requested.
7519          * For example, el.getStyles('color', 'font-size', 'width') might return
7520          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7521          * @param {String} style1 A style name
7522          * @param {String} style2 A style name
7523          * @param {String} etc.
7524          * @return {Object} The style object
7525          */
7526         getStyles : function(){
7527             var a = arguments, len = a.length, r = {};
7528             for(var i = 0; i < len; i++){
7529                 r[a[i]] = this.getStyle(a[i]);
7530             }
7531             return r;
7532         },
7533
7534         /**
7535          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7536          * @param {String} property The style property whose value is returned.
7537          * @return {String} The current value of the style property for this element.
7538          */
7539         getStyle : function(){
7540             return view && view.getComputedStyle ?
7541                 function(prop){
7542                     var el = this.dom, v, cs, camel;
7543                     if(prop == 'float'){
7544                         prop = "cssFloat";
7545                     }
7546                     if(el.style && (v = el.style[prop])){
7547                         return v;
7548                     }
7549                     if(cs = view.getComputedStyle(el, "")){
7550                         if(!(camel = propCache[prop])){
7551                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7552                         }
7553                         return cs[camel];
7554                     }
7555                     return null;
7556                 } :
7557                 function(prop){
7558                     var el = this.dom, v, cs, camel;
7559                     if(prop == 'opacity'){
7560                         if(typeof el.style.filter == 'string'){
7561                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7562                             if(m){
7563                                 var fv = parseFloat(m[1]);
7564                                 if(!isNaN(fv)){
7565                                     return fv ? fv / 100 : 0;
7566                                 }
7567                             }
7568                         }
7569                         return 1;
7570                     }else if(prop == 'float'){
7571                         prop = "styleFloat";
7572                     }
7573                     if(!(camel = propCache[prop])){
7574                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7575                     }
7576                     if(v = el.style[camel]){
7577                         return v;
7578                     }
7579                     if(cs = el.currentStyle){
7580                         return cs[camel];
7581                     }
7582                     return null;
7583                 };
7584         }(),
7585
7586         /**
7587          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7588          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7589          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7590          * @return {Roo.Element} this
7591          */
7592         setStyle : function(prop, value){
7593             if(typeof prop == "string"){
7594                 
7595                 if (prop == 'float') {
7596                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7597                     return this;
7598                 }
7599                 
7600                 var camel;
7601                 if(!(camel = propCache[prop])){
7602                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7603                 }
7604                 
7605                 if(camel == 'opacity') {
7606                     this.setOpacity(value);
7607                 }else{
7608                     this.dom.style[camel] = value;
7609                 }
7610             }else{
7611                 for(var style in prop){
7612                     if(typeof prop[style] != "function"){
7613                        this.setStyle(style, prop[style]);
7614                     }
7615                 }
7616             }
7617             return this;
7618         },
7619
7620         /**
7621          * More flexible version of {@link #setStyle} for setting style properties.
7622          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7623          * a function which returns such a specification.
7624          * @return {Roo.Element} this
7625          */
7626         applyStyles : function(style){
7627             Roo.DomHelper.applyStyles(this.dom, style);
7628             return this;
7629         },
7630
7631         /**
7632           * 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).
7633           * @return {Number} The X position of the element
7634           */
7635         getX : function(){
7636             return D.getX(this.dom);
7637         },
7638
7639         /**
7640           * 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).
7641           * @return {Number} The Y position of the element
7642           */
7643         getY : function(){
7644             return D.getY(this.dom);
7645         },
7646
7647         /**
7648           * 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).
7649           * @return {Array} The XY position of the element
7650           */
7651         getXY : function(){
7652             return D.getXY(this.dom);
7653         },
7654
7655         /**
7656          * 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).
7657          * @param {Number} The X position of the element
7658          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7659          * @return {Roo.Element} this
7660          */
7661         setX : function(x, animate){
7662             if(!animate || !A){
7663                 D.setX(this.dom, x);
7664             }else{
7665                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7666             }
7667             return this;
7668         },
7669
7670         /**
7671          * 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).
7672          * @param {Number} The Y position of the element
7673          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7674          * @return {Roo.Element} this
7675          */
7676         setY : function(y, animate){
7677             if(!animate || !A){
7678                 D.setY(this.dom, y);
7679             }else{
7680                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7681             }
7682             return this;
7683         },
7684
7685         /**
7686          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7687          * @param {String} left The left CSS property value
7688          * @return {Roo.Element} this
7689          */
7690         setLeft : function(left){
7691             this.setStyle("left", this.addUnits(left));
7692             return this;
7693         },
7694
7695         /**
7696          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7697          * @param {String} top The top CSS property value
7698          * @return {Roo.Element} this
7699          */
7700         setTop : function(top){
7701             this.setStyle("top", this.addUnits(top));
7702             return this;
7703         },
7704
7705         /**
7706          * Sets the element's CSS right style.
7707          * @param {String} right The right CSS property value
7708          * @return {Roo.Element} this
7709          */
7710         setRight : function(right){
7711             this.setStyle("right", this.addUnits(right));
7712             return this;
7713         },
7714
7715         /**
7716          * Sets the element's CSS bottom style.
7717          * @param {String} bottom The bottom CSS property value
7718          * @return {Roo.Element} this
7719          */
7720         setBottom : function(bottom){
7721             this.setStyle("bottom", this.addUnits(bottom));
7722             return this;
7723         },
7724
7725         /**
7726          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7727          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7728          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7729          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7730          * @return {Roo.Element} this
7731          */
7732         setXY : function(pos, animate){
7733             if(!animate || !A){
7734                 D.setXY(this.dom, pos);
7735             }else{
7736                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7737             }
7738             return this;
7739         },
7740
7741         /**
7742          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7743          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7744          * @param {Number} x X value for new position (coordinates are page-based)
7745          * @param {Number} y Y value for new position (coordinates are page-based)
7746          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7747          * @return {Roo.Element} this
7748          */
7749         setLocation : function(x, y, animate){
7750             this.setXY([x, y], this.preanim(arguments, 2));
7751             return this;
7752         },
7753
7754         /**
7755          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7756          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7757          * @param {Number} x X value for new position (coordinates are page-based)
7758          * @param {Number} y Y value for new position (coordinates are page-based)
7759          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7760          * @return {Roo.Element} this
7761          */
7762         moveTo : function(x, y, animate){
7763             this.setXY([x, y], this.preanim(arguments, 2));
7764             return this;
7765         },
7766
7767         /**
7768          * Returns the region of the given element.
7769          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7770          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7771          */
7772         getRegion : function(){
7773             return D.getRegion(this.dom);
7774         },
7775
7776         /**
7777          * Returns the offset height of the element
7778          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7779          * @return {Number} The element's height
7780          */
7781         getHeight : function(contentHeight){
7782             var h = this.dom.offsetHeight || 0;
7783             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7784         },
7785
7786         /**
7787          * Returns the offset width of the element
7788          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7789          * @return {Number} The element's width
7790          */
7791         getWidth : function(contentWidth){
7792             var w = this.dom.offsetWidth || 0;
7793             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7794         },
7795
7796         /**
7797          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7798          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7799          * if a height has not been set using CSS.
7800          * @return {Number}
7801          */
7802         getComputedHeight : function(){
7803             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7804             if(!h){
7805                 h = parseInt(this.getStyle('height'), 10) || 0;
7806                 if(!this.isBorderBox()){
7807                     h += this.getFrameWidth('tb');
7808                 }
7809             }
7810             return h;
7811         },
7812
7813         /**
7814          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7815          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7816          * if a width has not been set using CSS.
7817          * @return {Number}
7818          */
7819         getComputedWidth : function(){
7820             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7821             if(!w){
7822                 w = parseInt(this.getStyle('width'), 10) || 0;
7823                 if(!this.isBorderBox()){
7824                     w += this.getFrameWidth('lr');
7825                 }
7826             }
7827             return w;
7828         },
7829
7830         /**
7831          * Returns the size of the element.
7832          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7833          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7834          */
7835         getSize : function(contentSize){
7836             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7837         },
7838
7839         /**
7840          * Returns the width and height of the viewport.
7841          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7842          */
7843         getViewSize : function(){
7844             var d = this.dom, doc = document, aw = 0, ah = 0;
7845             if(d == doc || d == doc.body){
7846                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7847             }else{
7848                 return {
7849                     width : d.clientWidth,
7850                     height: d.clientHeight
7851                 };
7852             }
7853         },
7854
7855         /**
7856          * Returns the value of the "value" attribute
7857          * @param {Boolean} asNumber true to parse the value as a number
7858          * @return {String/Number}
7859          */
7860         getValue : function(asNumber){
7861             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7862         },
7863
7864         // private
7865         adjustWidth : function(width){
7866             if(typeof width == "number"){
7867                 if(this.autoBoxAdjust && !this.isBorderBox()){
7868                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7869                 }
7870                 if(width < 0){
7871                     width = 0;
7872                 }
7873             }
7874             return width;
7875         },
7876
7877         // private
7878         adjustHeight : function(height){
7879             if(typeof height == "number"){
7880                if(this.autoBoxAdjust && !this.isBorderBox()){
7881                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7882                }
7883                if(height < 0){
7884                    height = 0;
7885                }
7886             }
7887             return height;
7888         },
7889
7890         /**
7891          * Set the width of the element
7892          * @param {Number} width The new width
7893          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7894          * @return {Roo.Element} this
7895          */
7896         setWidth : function(width, animate){
7897             width = this.adjustWidth(width);
7898             if(!animate || !A){
7899                 this.dom.style.width = this.addUnits(width);
7900             }else{
7901                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Set the height of the element
7908          * @param {Number} height The new height
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912          setHeight : function(height, animate){
7913             height = this.adjustHeight(height);
7914             if(!animate || !A){
7915                 this.dom.style.height = this.addUnits(height);
7916             }else{
7917                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7918             }
7919             return this;
7920         },
7921
7922         /**
7923          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7924          * @param {Number} width The new width
7925          * @param {Number} height The new height
7926          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7927          * @return {Roo.Element} this
7928          */
7929          setSize : function(width, height, animate){
7930             if(typeof width == "object"){ // in case of object from getSize()
7931                 height = width.height; width = width.width;
7932             }
7933             width = this.adjustWidth(width); height = this.adjustHeight(height);
7934             if(!animate || !A){
7935                 this.dom.style.width = this.addUnits(width);
7936                 this.dom.style.height = this.addUnits(height);
7937             }else{
7938                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7939             }
7940             return this;
7941         },
7942
7943         /**
7944          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7945          * @param {Number} x X value for new position (coordinates are page-based)
7946          * @param {Number} y Y value for new position (coordinates are page-based)
7947          * @param {Number} width The new width
7948          * @param {Number} height The new height
7949          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7950          * @return {Roo.Element} this
7951          */
7952         setBounds : function(x, y, width, height, animate){
7953             if(!animate || !A){
7954                 this.setSize(width, height);
7955                 this.setLocation(x, y);
7956             }else{
7957                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7958                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7959                               this.preanim(arguments, 4), 'motion');
7960             }
7961             return this;
7962         },
7963
7964         /**
7965          * 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.
7966          * @param {Roo.lib.Region} region The region to fill
7967          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7968          * @return {Roo.Element} this
7969          */
7970         setRegion : function(region, animate){
7971             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7972             return this;
7973         },
7974
7975         /**
7976          * Appends an event handler
7977          *
7978          * @param {String}   eventName     The type of event to append
7979          * @param {Function} fn        The method the event invokes
7980          * @param {Object} scope       (optional) The scope (this object) of the fn
7981          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7982          */
7983         addListener : function(eventName, fn, scope, options){
7984             if (this.dom) {
7985                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7986             }
7987         },
7988
7989         /**
7990          * Removes an event handler from this element
7991          * @param {String} eventName the type of event to remove
7992          * @param {Function} fn the method the event invokes
7993          * @return {Roo.Element} this
7994          */
7995         removeListener : function(eventName, fn){
7996             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7997             return this;
7998         },
7999
8000         /**
8001          * Removes all previous added listeners from this element
8002          * @return {Roo.Element} this
8003          */
8004         removeAllListeners : function(){
8005             E.purgeElement(this.dom);
8006             return this;
8007         },
8008
8009         relayEvent : function(eventName, observable){
8010             this.on(eventName, function(e){
8011                 observable.fireEvent(eventName, e);
8012             });
8013         },
8014
8015         /**
8016          * Set the opacity of the element
8017          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8018          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8019          * @return {Roo.Element} this
8020          */
8021          setOpacity : function(opacity, animate){
8022             if(!animate || !A){
8023                 var s = this.dom.style;
8024                 if(Roo.isIE){
8025                     s.zoom = 1;
8026                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8027                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8028                 }else{
8029                     s.opacity = opacity;
8030                 }
8031             }else{
8032                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8033             }
8034             return this;
8035         },
8036
8037         /**
8038          * Gets the left X coordinate
8039          * @param {Boolean} local True to get the local css position instead of page coordinate
8040          * @return {Number}
8041          */
8042         getLeft : function(local){
8043             if(!local){
8044                 return this.getX();
8045             }else{
8046                 return parseInt(this.getStyle("left"), 10) || 0;
8047             }
8048         },
8049
8050         /**
8051          * Gets the right X coordinate of the element (element X position + element width)
8052          * @param {Boolean} local True to get the local css position instead of page coordinate
8053          * @return {Number}
8054          */
8055         getRight : function(local){
8056             if(!local){
8057                 return this.getX() + this.getWidth();
8058             }else{
8059                 return (this.getLeft(true) + this.getWidth()) || 0;
8060             }
8061         },
8062
8063         /**
8064          * Gets the top Y coordinate
8065          * @param {Boolean} local True to get the local css position instead of page coordinate
8066          * @return {Number}
8067          */
8068         getTop : function(local) {
8069             if(!local){
8070                 return this.getY();
8071             }else{
8072                 return parseInt(this.getStyle("top"), 10) || 0;
8073             }
8074         },
8075
8076         /**
8077          * Gets the bottom Y coordinate of the element (element Y position + element height)
8078          * @param {Boolean} local True to get the local css position instead of page coordinate
8079          * @return {Number}
8080          */
8081         getBottom : function(local){
8082             if(!local){
8083                 return this.getY() + this.getHeight();
8084             }else{
8085                 return (this.getTop(true) + this.getHeight()) || 0;
8086             }
8087         },
8088
8089         /**
8090         * Initializes positioning on this element. If a desired position is not passed, it will make the
8091         * the element positioned relative IF it is not already positioned.
8092         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8093         * @param {Number} zIndex (optional) The zIndex to apply
8094         * @param {Number} x (optional) Set the page X position
8095         * @param {Number} y (optional) Set the page Y position
8096         */
8097         position : function(pos, zIndex, x, y){
8098             if(!pos){
8099                if(this.getStyle('position') == 'static'){
8100                    this.setStyle('position', 'relative');
8101                }
8102             }else{
8103                 this.setStyle("position", pos);
8104             }
8105             if(zIndex){
8106                 this.setStyle("z-index", zIndex);
8107             }
8108             if(x !== undefined && y !== undefined){
8109                 this.setXY([x, y]);
8110             }else if(x !== undefined){
8111                 this.setX(x);
8112             }else if(y !== undefined){
8113                 this.setY(y);
8114             }
8115         },
8116
8117         /**
8118         * Clear positioning back to the default when the document was loaded
8119         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8120         * @return {Roo.Element} this
8121          */
8122         clearPositioning : function(value){
8123             value = value ||'';
8124             this.setStyle({
8125                 "left": value,
8126                 "right": value,
8127                 "top": value,
8128                 "bottom": value,
8129                 "z-index": "",
8130                 "position" : "static"
8131             });
8132             return this;
8133         },
8134
8135         /**
8136         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8137         * snapshot before performing an update and then restoring the element.
8138         * @return {Object}
8139         */
8140         getPositioning : function(){
8141             var l = this.getStyle("left");
8142             var t = this.getStyle("top");
8143             return {
8144                 "position" : this.getStyle("position"),
8145                 "left" : l,
8146                 "right" : l ? "" : this.getStyle("right"),
8147                 "top" : t,
8148                 "bottom" : t ? "" : this.getStyle("bottom"),
8149                 "z-index" : this.getStyle("z-index")
8150             };
8151         },
8152
8153         /**
8154          * Gets the width of the border(s) for the specified side(s)
8155          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8156          * passing lr would get the border (l)eft width + the border (r)ight width.
8157          * @return {Number} The width of the sides passed added together
8158          */
8159         getBorderWidth : function(side){
8160             return this.addStyles(side, El.borders);
8161         },
8162
8163         /**
8164          * Gets the width of the padding(s) for the specified side(s)
8165          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8166          * passing lr would get the padding (l)eft + the padding (r)ight.
8167          * @return {Number} The padding of the sides passed added together
8168          */
8169         getPadding : function(side){
8170             return this.addStyles(side, El.paddings);
8171         },
8172
8173         /**
8174         * Set positioning with an object returned by getPositioning().
8175         * @param {Object} posCfg
8176         * @return {Roo.Element} this
8177          */
8178         setPositioning : function(pc){
8179             this.applyStyles(pc);
8180             if(pc.right == "auto"){
8181                 this.dom.style.right = "";
8182             }
8183             if(pc.bottom == "auto"){
8184                 this.dom.style.bottom = "";
8185             }
8186             return this;
8187         },
8188
8189         // private
8190         fixDisplay : function(){
8191             if(this.getStyle("display") == "none"){
8192                 this.setStyle("visibility", "hidden");
8193                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8194                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8195                     this.setStyle("display", "block");
8196                 }
8197             }
8198         },
8199
8200         /**
8201          * Quick set left and top adding default units
8202          * @param {String} left The left CSS property value
8203          * @param {String} top The top CSS property value
8204          * @return {Roo.Element} this
8205          */
8206          setLeftTop : function(left, top){
8207             this.dom.style.left = this.addUnits(left);
8208             this.dom.style.top = this.addUnits(top);
8209             return this;
8210         },
8211
8212         /**
8213          * Move this element relative to its current position.
8214          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8215          * @param {Number} distance How far to move the element in pixels
8216          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8217          * @return {Roo.Element} this
8218          */
8219          move : function(direction, distance, animate){
8220             var xy = this.getXY();
8221             direction = direction.toLowerCase();
8222             switch(direction){
8223                 case "l":
8224                 case "left":
8225                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8226                     break;
8227                case "r":
8228                case "right":
8229                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8230                     break;
8231                case "t":
8232                case "top":
8233                case "up":
8234                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8235                     break;
8236                case "b":
8237                case "bottom":
8238                case "down":
8239                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8240                     break;
8241             }
8242             return this;
8243         },
8244
8245         /**
8246          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8247          * @return {Roo.Element} this
8248          */
8249         clip : function(){
8250             if(!this.isClipped){
8251                this.isClipped = true;
8252                this.originalClip = {
8253                    "o": this.getStyle("overflow"),
8254                    "x": this.getStyle("overflow-x"),
8255                    "y": this.getStyle("overflow-y")
8256                };
8257                this.setStyle("overflow", "hidden");
8258                this.setStyle("overflow-x", "hidden");
8259                this.setStyle("overflow-y", "hidden");
8260             }
8261             return this;
8262         },
8263
8264         /**
8265          *  Return clipping (overflow) to original clipping before clip() was called
8266          * @return {Roo.Element} this
8267          */
8268         unclip : function(){
8269             if(this.isClipped){
8270                 this.isClipped = false;
8271                 var o = this.originalClip;
8272                 if(o.o){this.setStyle("overflow", o.o);}
8273                 if(o.x){this.setStyle("overflow-x", o.x);}
8274                 if(o.y){this.setStyle("overflow-y", o.y);}
8275             }
8276             return this;
8277         },
8278
8279
8280         /**
8281          * Gets the x,y coordinates specified by the anchor position on the element.
8282          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8283          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8284          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8285          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8286          * @return {Array} [x, y] An array containing the element's x and y coordinates
8287          */
8288         getAnchorXY : function(anchor, local, s){
8289             //Passing a different size is useful for pre-calculating anchors,
8290             //especially for anchored animations that change the el size.
8291
8292             var w, h, vp = false;
8293             if(!s){
8294                 var d = this.dom;
8295                 if(d == document.body || d == document){
8296                     vp = true;
8297                     w = D.getViewWidth(); h = D.getViewHeight();
8298                 }else{
8299                     w = this.getWidth(); h = this.getHeight();
8300                 }
8301             }else{
8302                 w = s.width;  h = s.height;
8303             }
8304             var x = 0, y = 0, r = Math.round;
8305             switch((anchor || "tl").toLowerCase()){
8306                 case "c":
8307                     x = r(w*.5);
8308                     y = r(h*.5);
8309                 break;
8310                 case "t":
8311                     x = r(w*.5);
8312                     y = 0;
8313                 break;
8314                 case "l":
8315                     x = 0;
8316                     y = r(h*.5);
8317                 break;
8318                 case "r":
8319                     x = w;
8320                     y = r(h*.5);
8321                 break;
8322                 case "b":
8323                     x = r(w*.5);
8324                     y = h;
8325                 break;
8326                 case "tl":
8327                     x = 0;
8328                     y = 0;
8329                 break;
8330                 case "bl":
8331                     x = 0;
8332                     y = h;
8333                 break;
8334                 case "br":
8335                     x = w;
8336                     y = h;
8337                 break;
8338                 case "tr":
8339                     x = w;
8340                     y = 0;
8341                 break;
8342             }
8343             if(local === true){
8344                 return [x, y];
8345             }
8346             if(vp){
8347                 var sc = this.getScroll();
8348                 return [x + sc.left, y + sc.top];
8349             }
8350             //Add the element's offset xy
8351             var o = this.getXY();
8352             return [x+o[0], y+o[1]];
8353         },
8354
8355         /**
8356          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8357          * supported position values.
8358          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8359          * @param {String} position The position to align to.
8360          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8361          * @return {Array} [x, y]
8362          */
8363         getAlignToXY : function(el, p, o){
8364             el = Roo.get(el);
8365             var d = this.dom;
8366             if(!el.dom){
8367                 throw "Element.alignTo with an element that doesn't exist";
8368             }
8369             var c = false; //constrain to viewport
8370             var p1 = "", p2 = "";
8371             o = o || [0,0];
8372
8373             if(!p){
8374                 p = "tl-bl";
8375             }else if(p == "?"){
8376                 p = "tl-bl?";
8377             }else if(p.indexOf("-") == -1){
8378                 p = "tl-" + p;
8379             }
8380             p = p.toLowerCase();
8381             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8382             if(!m){
8383                throw "Element.alignTo with an invalid alignment " + p;
8384             }
8385             p1 = m[1]; p2 = m[2]; c = !!m[3];
8386
8387             //Subtract the aligned el's internal xy from the target's offset xy
8388             //plus custom offset to get the aligned el's new offset xy
8389             var a1 = this.getAnchorXY(p1, true);
8390             var a2 = el.getAnchorXY(p2, false);
8391             var x = a2[0] - a1[0] + o[0];
8392             var y = a2[1] - a1[1] + o[1];
8393             if(c){
8394                 //constrain the aligned el to viewport if necessary
8395                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8396                 // 5px of margin for ie
8397                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8398
8399                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8400                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8401                 //otherwise swap the aligned el to the opposite border of the target.
8402                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8403                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8404                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8405                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8406
8407                var doc = document;
8408                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8409                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8410
8411                if((x+w) > dw + scrollX){
8412                     x = swapX ? r.left-w : dw+scrollX-w;
8413                 }
8414                if(x < scrollX){
8415                    x = swapX ? r.right : scrollX;
8416                }
8417                if((y+h) > dh + scrollY){
8418                     y = swapY ? r.top-h : dh+scrollY-h;
8419                 }
8420                if (y < scrollY){
8421                    y = swapY ? r.bottom : scrollY;
8422                }
8423             }
8424             return [x,y];
8425         },
8426
8427         // private
8428         getConstrainToXY : function(){
8429             var os = {top:0, left:0, bottom:0, right: 0};
8430
8431             return function(el, local, offsets, proposedXY){
8432                 el = Roo.get(el);
8433                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8434
8435                 var vw, vh, vx = 0, vy = 0;
8436                 if(el.dom == document.body || el.dom == document){
8437                     vw = Roo.lib.Dom.getViewWidth();
8438                     vh = Roo.lib.Dom.getViewHeight();
8439                 }else{
8440                     vw = el.dom.clientWidth;
8441                     vh = el.dom.clientHeight;
8442                     if(!local){
8443                         var vxy = el.getXY();
8444                         vx = vxy[0];
8445                         vy = vxy[1];
8446                     }
8447                 }
8448
8449                 var s = el.getScroll();
8450
8451                 vx += offsets.left + s.left;
8452                 vy += offsets.top + s.top;
8453
8454                 vw -= offsets.right;
8455                 vh -= offsets.bottom;
8456
8457                 var vr = vx+vw;
8458                 var vb = vy+vh;
8459
8460                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8461                 var x = xy[0], y = xy[1];
8462                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8463
8464                 // only move it if it needs it
8465                 var moved = false;
8466
8467                 // first validate right/bottom
8468                 if((x + w) > vr){
8469                     x = vr - w;
8470                     moved = true;
8471                 }
8472                 if((y + h) > vb){
8473                     y = vb - h;
8474                     moved = true;
8475                 }
8476                 // then make sure top/left isn't negative
8477                 if(x < vx){
8478                     x = vx;
8479                     moved = true;
8480                 }
8481                 if(y < vy){
8482                     y = vy;
8483                     moved = true;
8484                 }
8485                 return moved ? [x, y] : false;
8486             };
8487         }(),
8488
8489         // private
8490         adjustForConstraints : function(xy, parent, offsets){
8491             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8492         },
8493
8494         /**
8495          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8496          * document it aligns it to the viewport.
8497          * The position parameter is optional, and can be specified in any one of the following formats:
8498          * <ul>
8499          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8500          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8501          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8502          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8503          *   <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
8504          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8505          * </ul>
8506          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8507          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8508          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8509          * that specified in order to enforce the viewport constraints.
8510          * Following are all of the supported anchor positions:
8511     <pre>
8512     Value  Description
8513     -----  -----------------------------
8514     tl     The top left corner (default)
8515     t      The center of the top edge
8516     tr     The top right corner
8517     l      The center of the left edge
8518     c      In the center of the element
8519     r      The center of the right edge
8520     bl     The bottom left corner
8521     b      The center of the bottom edge
8522     br     The bottom right corner
8523     </pre>
8524     Example Usage:
8525     <pre><code>
8526     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8527     el.alignTo("other-el");
8528
8529     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8530     el.alignTo("other-el", "tr?");
8531
8532     // align the bottom right corner of el with the center left edge of other-el
8533     el.alignTo("other-el", "br-l?");
8534
8535     // align the center of el with the bottom left corner of other-el and
8536     // adjust the x position by -6 pixels (and the y position by 0)
8537     el.alignTo("other-el", "c-bl", [-6, 0]);
8538     </code></pre>
8539          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8540          * @param {String} position The position to align to.
8541          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8542          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8543          * @return {Roo.Element} this
8544          */
8545         alignTo : function(element, position, offsets, animate){
8546             var xy = this.getAlignToXY(element, position, offsets);
8547             this.setXY(xy, this.preanim(arguments, 3));
8548             return this;
8549         },
8550
8551         /**
8552          * Anchors an element to another element and realigns it when the window is resized.
8553          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8554          * @param {String} position The position to align to.
8555          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8556          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8557          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8558          * is a number, it is used as the buffer delay (defaults to 50ms).
8559          * @param {Function} callback The function to call after the animation finishes
8560          * @return {Roo.Element} this
8561          */
8562         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8563             var action = function(){
8564                 this.alignTo(el, alignment, offsets, animate);
8565                 Roo.callback(callback, this);
8566             };
8567             Roo.EventManager.onWindowResize(action, this);
8568             var tm = typeof monitorScroll;
8569             if(tm != 'undefined'){
8570                 Roo.EventManager.on(window, 'scroll', action, this,
8571                     {buffer: tm == 'number' ? monitorScroll : 50});
8572             }
8573             action.call(this); // align immediately
8574             return this;
8575         },
8576         /**
8577          * Clears any opacity settings from this element. Required in some cases for IE.
8578          * @return {Roo.Element} this
8579          */
8580         clearOpacity : function(){
8581             if (window.ActiveXObject) {
8582                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8583                     this.dom.style.filter = "";
8584                 }
8585             } else {
8586                 this.dom.style.opacity = "";
8587                 this.dom.style["-moz-opacity"] = "";
8588                 this.dom.style["-khtml-opacity"] = "";
8589             }
8590             return this;
8591         },
8592
8593         /**
8594          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8595          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8596          * @return {Roo.Element} this
8597          */
8598         hide : function(animate){
8599             this.setVisible(false, this.preanim(arguments, 0));
8600             return this;
8601         },
8602
8603         /**
8604         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8605         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8606          * @return {Roo.Element} this
8607          */
8608         show : function(animate){
8609             this.setVisible(true, this.preanim(arguments, 0));
8610             return this;
8611         },
8612
8613         /**
8614          * @private Test if size has a unit, otherwise appends the default
8615          */
8616         addUnits : function(size){
8617             return Roo.Element.addUnits(size, this.defaultUnit);
8618         },
8619
8620         /**
8621          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8622          * @return {Roo.Element} this
8623          */
8624         beginMeasure : function(){
8625             var el = this.dom;
8626             if(el.offsetWidth || el.offsetHeight){
8627                 return this; // offsets work already
8628             }
8629             var changed = [];
8630             var p = this.dom, b = document.body; // start with this element
8631             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8632                 var pe = Roo.get(p);
8633                 if(pe.getStyle('display') == 'none'){
8634                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8635                     p.style.visibility = "hidden";
8636                     p.style.display = "block";
8637                 }
8638                 p = p.parentNode;
8639             }
8640             this._measureChanged = changed;
8641             return this;
8642
8643         },
8644
8645         /**
8646          * Restores displays to before beginMeasure was called
8647          * @return {Roo.Element} this
8648          */
8649         endMeasure : function(){
8650             var changed = this._measureChanged;
8651             if(changed){
8652                 for(var i = 0, len = changed.length; i < len; i++) {
8653                     var r = changed[i];
8654                     r.el.style.visibility = r.visibility;
8655                     r.el.style.display = "none";
8656                 }
8657                 this._measureChanged = null;
8658             }
8659             return this;
8660         },
8661
8662         /**
8663         * Update the innerHTML of this element, optionally searching for and processing scripts
8664         * @param {String} html The new HTML
8665         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8666         * @param {Function} callback For async script loading you can be noticed when the update completes
8667         * @return {Roo.Element} this
8668          */
8669         update : function(html, loadScripts, callback){
8670             if(typeof html == "undefined"){
8671                 html = "";
8672             }
8673             if(loadScripts !== true){
8674                 this.dom.innerHTML = html;
8675                 if(typeof callback == "function"){
8676                     callback();
8677                 }
8678                 return this;
8679             }
8680             var id = Roo.id();
8681             var dom = this.dom;
8682
8683             html += '<span id="' + id + '"></span>';
8684
8685             E.onAvailable(id, function(){
8686                 var hd = document.getElementsByTagName("head")[0];
8687                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8688                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8689                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8690
8691                 var match;
8692                 while(match = re.exec(html)){
8693                     var attrs = match[1];
8694                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8695                     if(srcMatch && srcMatch[2]){
8696                        var s = document.createElement("script");
8697                        s.src = srcMatch[2];
8698                        var typeMatch = attrs.match(typeRe);
8699                        if(typeMatch && typeMatch[2]){
8700                            s.type = typeMatch[2];
8701                        }
8702                        hd.appendChild(s);
8703                     }else if(match[2] && match[2].length > 0){
8704                         if(window.execScript) {
8705                            window.execScript(match[2]);
8706                         } else {
8707                             /**
8708                              * eval:var:id
8709                              * eval:var:dom
8710                              * eval:var:html
8711                              * 
8712                              */
8713                            window.eval(match[2]);
8714                         }
8715                     }
8716                 }
8717                 var el = document.getElementById(id);
8718                 if(el){el.parentNode.removeChild(el);}
8719                 if(typeof callback == "function"){
8720                     callback();
8721                 }
8722             });
8723             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8724             return this;
8725         },
8726
8727         /**
8728          * Direct access to the UpdateManager update() method (takes the same parameters).
8729          * @param {String/Function} url The url for this request or a function to call to get the url
8730          * @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}
8731          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8732          * @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.
8733          * @return {Roo.Element} this
8734          */
8735         load : function(){
8736             var um = this.getUpdateManager();
8737             um.update.apply(um, arguments);
8738             return this;
8739         },
8740
8741         /**
8742         * Gets this element's UpdateManager
8743         * @return {Roo.UpdateManager} The UpdateManager
8744         */
8745         getUpdateManager : function(){
8746             if(!this.updateManager){
8747                 this.updateManager = new Roo.UpdateManager(this);
8748             }
8749             return this.updateManager;
8750         },
8751
8752         /**
8753          * Disables text selection for this element (normalized across browsers)
8754          * @return {Roo.Element} this
8755          */
8756         unselectable : function(){
8757             this.dom.unselectable = "on";
8758             this.swallowEvent("selectstart", true);
8759             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8760             this.addClass("x-unselectable");
8761             return this;
8762         },
8763
8764         /**
8765         * Calculates the x, y to center this element on the screen
8766         * @return {Array} The x, y values [x, y]
8767         */
8768         getCenterXY : function(){
8769             return this.getAlignToXY(document, 'c-c');
8770         },
8771
8772         /**
8773         * Centers the Element in either the viewport, or another Element.
8774         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8775         */
8776         center : function(centerIn){
8777             this.alignTo(centerIn || document, 'c-c');
8778             return this;
8779         },
8780
8781         /**
8782          * Tests various css rules/browsers to determine if this element uses a border box
8783          * @return {Boolean}
8784          */
8785         isBorderBox : function(){
8786             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8787         },
8788
8789         /**
8790          * Return a box {x, y, width, height} that can be used to set another elements
8791          * size/location to match this element.
8792          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8793          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8794          * @return {Object} box An object in the format {x, y, width, height}
8795          */
8796         getBox : function(contentBox, local){
8797             var xy;
8798             if(!local){
8799                 xy = this.getXY();
8800             }else{
8801                 var left = parseInt(this.getStyle("left"), 10) || 0;
8802                 var top = parseInt(this.getStyle("top"), 10) || 0;
8803                 xy = [left, top];
8804             }
8805             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8806             if(!contentBox){
8807                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8808             }else{
8809                 var l = this.getBorderWidth("l")+this.getPadding("l");
8810                 var r = this.getBorderWidth("r")+this.getPadding("r");
8811                 var t = this.getBorderWidth("t")+this.getPadding("t");
8812                 var b = this.getBorderWidth("b")+this.getPadding("b");
8813                 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)};
8814             }
8815             bx.right = bx.x + bx.width;
8816             bx.bottom = bx.y + bx.height;
8817             return bx;
8818         },
8819
8820         /**
8821          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8822          for more information about the sides.
8823          * @param {String} sides
8824          * @return {Number}
8825          */
8826         getFrameWidth : function(sides, onlyContentBox){
8827             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8828         },
8829
8830         /**
8831          * 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.
8832          * @param {Object} box The box to fill {x, y, width, height}
8833          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8834          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8835          * @return {Roo.Element} this
8836          */
8837         setBox : function(box, adjust, animate){
8838             var w = box.width, h = box.height;
8839             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8840                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8841                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8842             }
8843             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8844             return this;
8845         },
8846
8847         /**
8848          * Forces the browser to repaint this element
8849          * @return {Roo.Element} this
8850          */
8851          repaint : function(){
8852             var dom = this.dom;
8853             this.addClass("x-repaint");
8854             setTimeout(function(){
8855                 Roo.get(dom).removeClass("x-repaint");
8856             }, 1);
8857             return this;
8858         },
8859
8860         /**
8861          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8862          * then it returns the calculated width of the sides (see getPadding)
8863          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8864          * @return {Object/Number}
8865          */
8866         getMargins : function(side){
8867             if(!side){
8868                 return {
8869                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8870                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8871                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8872                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8873                 };
8874             }else{
8875                 return this.addStyles(side, El.margins);
8876              }
8877         },
8878
8879         // private
8880         addStyles : function(sides, styles){
8881             var val = 0, v, w;
8882             for(var i = 0, len = sides.length; i < len; i++){
8883                 v = this.getStyle(styles[sides.charAt(i)]);
8884                 if(v){
8885                      w = parseInt(v, 10);
8886                      if(w){ val += w; }
8887                 }
8888             }
8889             return val;
8890         },
8891
8892         /**
8893          * Creates a proxy element of this element
8894          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8895          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8896          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8897          * @return {Roo.Element} The new proxy element
8898          */
8899         createProxy : function(config, renderTo, matchBox){
8900             if(renderTo){
8901                 renderTo = Roo.getDom(renderTo);
8902             }else{
8903                 renderTo = document.body;
8904             }
8905             config = typeof config == "object" ?
8906                 config : {tag : "div", cls: config};
8907             var proxy = Roo.DomHelper.append(renderTo, config, true);
8908             if(matchBox){
8909                proxy.setBox(this.getBox());
8910             }
8911             return proxy;
8912         },
8913
8914         /**
8915          * Puts a mask over this element to disable user interaction. Requires core.css.
8916          * This method can only be applied to elements which accept child nodes.
8917          * @param {String} msg (optional) A message to display in the mask
8918          * @param {String} msgCls (optional) A css class to apply to the msg element
8919          * @return {Element} The mask  element
8920          */
8921         mask : function(msg, msgCls)
8922         {
8923             if(this.getStyle("position") == "static"){
8924                 this.setStyle("position", "relative");
8925             }
8926             if(!this._mask){
8927                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8928             }
8929             this.addClass("x-masked");
8930             this._mask.setDisplayed(true);
8931             
8932             // we wander
8933             var z = 0;
8934             var dom = this.dom
8935             while (dom && dom.style) {
8936                 if (!isNaN(parseInt(dom.style.zIndex))) {
8937                     z = Math.max(z, parseInt(dom.style.zIndex));
8938                 }
8939                 dom = dom.parentNode;
8940             }
8941             // if we are masking the body - then it hides everything..
8942             if (this.dom == document.body) {
8943                 z = 1000000;
8944                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8945                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8946             }
8947            
8948             if(typeof msg == 'string'){
8949                 if(!this._maskMsg){
8950                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8951                 }
8952                 var mm = this._maskMsg;
8953                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8954                 mm.dom.firstChild.innerHTML = msg;
8955                 mm.setDisplayed(true);
8956                 mm.center(this);
8957                 mm.setStyle('z-index', z + 102);
8958             }
8959             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8960                 this._mask.setHeight(this.getHeight());
8961             }
8962             this._mask.setStyle('z-index', z + 100);
8963             
8964             return this._mask;
8965         },
8966
8967         /**
8968          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8969          * it is cached for reuse.
8970          */
8971         unmask : function(removeEl){
8972             if(this._mask){
8973                 if(removeEl === true){
8974                     this._mask.remove();
8975                     delete this._mask;
8976                     if(this._maskMsg){
8977                         this._maskMsg.remove();
8978                         delete this._maskMsg;
8979                     }
8980                 }else{
8981                     this._mask.setDisplayed(false);
8982                     if(this._maskMsg){
8983                         this._maskMsg.setDisplayed(false);
8984                     }
8985                 }
8986             }
8987             this.removeClass("x-masked");
8988         },
8989
8990         /**
8991          * Returns true if this element is masked
8992          * @return {Boolean}
8993          */
8994         isMasked : function(){
8995             return this._mask && this._mask.isVisible();
8996         },
8997
8998         /**
8999          * Creates an iframe shim for this element to keep selects and other windowed objects from
9000          * showing through.
9001          * @return {Roo.Element} The new shim element
9002          */
9003         createShim : function(){
9004             var el = document.createElement('iframe');
9005             el.frameBorder = 'no';
9006             el.className = 'roo-shim';
9007             if(Roo.isIE && Roo.isSecure){
9008                 el.src = Roo.SSL_SECURE_URL;
9009             }
9010             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9011             shim.autoBoxAdjust = false;
9012             return shim;
9013         },
9014
9015         /**
9016          * Removes this element from the DOM and deletes it from the cache
9017          */
9018         remove : function(){
9019             if(this.dom.parentNode){
9020                 this.dom.parentNode.removeChild(this.dom);
9021             }
9022             delete El.cache[this.dom.id];
9023         },
9024
9025         /**
9026          * Sets up event handlers to add and remove a css class when the mouse is over this element
9027          * @param {String} className
9028          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9029          * mouseout events for children elements
9030          * @return {Roo.Element} this
9031          */
9032         addClassOnOver : function(className, preventFlicker){
9033             this.on("mouseover", function(){
9034                 Roo.fly(this, '_internal').addClass(className);
9035             }, this.dom);
9036             var removeFn = function(e){
9037                 if(preventFlicker !== true || !e.within(this, true)){
9038                     Roo.fly(this, '_internal').removeClass(className);
9039                 }
9040             };
9041             this.on("mouseout", removeFn, this.dom);
9042             return this;
9043         },
9044
9045         /**
9046          * Sets up event handlers to add and remove a css class when this element has the focus
9047          * @param {String} className
9048          * @return {Roo.Element} this
9049          */
9050         addClassOnFocus : function(className){
9051             this.on("focus", function(){
9052                 Roo.fly(this, '_internal').addClass(className);
9053             }, this.dom);
9054             this.on("blur", function(){
9055                 Roo.fly(this, '_internal').removeClass(className);
9056             }, this.dom);
9057             return this;
9058         },
9059         /**
9060          * 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)
9061          * @param {String} className
9062          * @return {Roo.Element} this
9063          */
9064         addClassOnClick : function(className){
9065             var dom = this.dom;
9066             this.on("mousedown", function(){
9067                 Roo.fly(dom, '_internal').addClass(className);
9068                 var d = Roo.get(document);
9069                 var fn = function(){
9070                     Roo.fly(dom, '_internal').removeClass(className);
9071                     d.removeListener("mouseup", fn);
9072                 };
9073                 d.on("mouseup", fn);
9074             });
9075             return this;
9076         },
9077
9078         /**
9079          * Stops the specified event from bubbling and optionally prevents the default action
9080          * @param {String} eventName
9081          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9082          * @return {Roo.Element} this
9083          */
9084         swallowEvent : function(eventName, preventDefault){
9085             var fn = function(e){
9086                 e.stopPropagation();
9087                 if(preventDefault){
9088                     e.preventDefault();
9089                 }
9090             };
9091             if(eventName instanceof Array){
9092                 for(var i = 0, len = eventName.length; i < len; i++){
9093                      this.on(eventName[i], fn);
9094                 }
9095                 return this;
9096             }
9097             this.on(eventName, fn);
9098             return this;
9099         },
9100
9101         /**
9102          * @private
9103          */
9104       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9105
9106         /**
9107          * Sizes this element to its parent element's dimensions performing
9108          * neccessary box adjustments.
9109          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9110          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9111          * @return {Roo.Element} this
9112          */
9113         fitToParent : function(monitorResize, targetParent) {
9114           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9115           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9116           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9117             return;
9118           }
9119           var p = Roo.get(targetParent || this.dom.parentNode);
9120           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9121           if (monitorResize === true) {
9122             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9123             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9124           }
9125           return this;
9126         },
9127
9128         /**
9129          * Gets the next sibling, skipping text nodes
9130          * @return {HTMLElement} The next sibling or null
9131          */
9132         getNextSibling : function(){
9133             var n = this.dom.nextSibling;
9134             while(n && n.nodeType != 1){
9135                 n = n.nextSibling;
9136             }
9137             return n;
9138         },
9139
9140         /**
9141          * Gets the previous sibling, skipping text nodes
9142          * @return {HTMLElement} The previous sibling or null
9143          */
9144         getPrevSibling : function(){
9145             var n = this.dom.previousSibling;
9146             while(n && n.nodeType != 1){
9147                 n = n.previousSibling;
9148             }
9149             return n;
9150         },
9151
9152
9153         /**
9154          * Appends the passed element(s) to this element
9155          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9156          * @return {Roo.Element} this
9157          */
9158         appendChild: function(el){
9159             el = Roo.get(el);
9160             el.appendTo(this);
9161             return this;
9162         },
9163
9164         /**
9165          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9166          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9167          * automatically generated with the specified attributes.
9168          * @param {HTMLElement} insertBefore (optional) a child element of this element
9169          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9170          * @return {Roo.Element} The new child element
9171          */
9172         createChild: function(config, insertBefore, returnDom){
9173             config = config || {tag:'div'};
9174             if(insertBefore){
9175                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9176             }
9177             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9178         },
9179
9180         /**
9181          * Appends this element to the passed element
9182          * @param {String/HTMLElement/Element} el The new parent element
9183          * @return {Roo.Element} this
9184          */
9185         appendTo: function(el){
9186             el = Roo.getDom(el);
9187             el.appendChild(this.dom);
9188             return this;
9189         },
9190
9191         /**
9192          * Inserts this element before the passed element in the DOM
9193          * @param {String/HTMLElement/Element} el The element to insert before
9194          * @return {Roo.Element} this
9195          */
9196         insertBefore: function(el){
9197             el = Roo.getDom(el);
9198             el.parentNode.insertBefore(this.dom, el);
9199             return this;
9200         },
9201
9202         /**
9203          * Inserts this element after the passed element in the DOM
9204          * @param {String/HTMLElement/Element} el The element to insert after
9205          * @return {Roo.Element} this
9206          */
9207         insertAfter: function(el){
9208             el = Roo.getDom(el);
9209             el.parentNode.insertBefore(this.dom, el.nextSibling);
9210             return this;
9211         },
9212
9213         /**
9214          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9215          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9216          * @return {Roo.Element} The new child
9217          */
9218         insertFirst: function(el, returnDom){
9219             el = el || {};
9220             if(typeof el == 'object' && !el.nodeType){ // dh config
9221                 return this.createChild(el, this.dom.firstChild, returnDom);
9222             }else{
9223                 el = Roo.getDom(el);
9224                 this.dom.insertBefore(el, this.dom.firstChild);
9225                 return !returnDom ? Roo.get(el) : el;
9226             }
9227         },
9228
9229         /**
9230          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9231          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9232          * @param {String} where (optional) 'before' or 'after' defaults to before
9233          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9234          * @return {Roo.Element} the inserted Element
9235          */
9236         insertSibling: function(el, where, returnDom){
9237             where = where ? where.toLowerCase() : 'before';
9238             el = el || {};
9239             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9240
9241             if(typeof el == 'object' && !el.nodeType){ // dh config
9242                 if(where == 'after' && !this.dom.nextSibling){
9243                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9244                 }else{
9245                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9246                 }
9247
9248             }else{
9249                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9250                             where == 'before' ? this.dom : this.dom.nextSibling);
9251                 if(!returnDom){
9252                     rt = Roo.get(rt);
9253                 }
9254             }
9255             return rt;
9256         },
9257
9258         /**
9259          * Creates and wraps this element with another element
9260          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9261          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9262          * @return {HTMLElement/Element} The newly created wrapper element
9263          */
9264         wrap: function(config, returnDom){
9265             if(!config){
9266                 config = {tag: "div"};
9267             }
9268             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9269             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9270             return newEl;
9271         },
9272
9273         /**
9274          * Replaces the passed element with this element
9275          * @param {String/HTMLElement/Element} el The element to replace
9276          * @return {Roo.Element} this
9277          */
9278         replace: function(el){
9279             el = Roo.get(el);
9280             this.insertBefore(el);
9281             el.remove();
9282             return this;
9283         },
9284
9285         /**
9286          * Inserts an html fragment into this element
9287          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9288          * @param {String} html The HTML fragment
9289          * @param {Boolean} returnEl True to return an Roo.Element
9290          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9291          */
9292         insertHtml : function(where, html, returnEl){
9293             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9294             return returnEl ? Roo.get(el) : el;
9295         },
9296
9297         /**
9298          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9299          * @param {Object} o The object with the attributes
9300          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9301          * @return {Roo.Element} this
9302          */
9303         set : function(o, useSet){
9304             var el = this.dom;
9305             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9306             for(var attr in o){
9307                 if(attr == "style" || typeof o[attr] == "function") continue;
9308                 if(attr=="cls"){
9309                     el.className = o["cls"];
9310                 }else{
9311                     if(useSet) el.setAttribute(attr, o[attr]);
9312                     else el[attr] = o[attr];
9313                 }
9314             }
9315             if(o.style){
9316                 Roo.DomHelper.applyStyles(el, o.style);
9317             }
9318             return this;
9319         },
9320
9321         /**
9322          * Convenience method for constructing a KeyMap
9323          * @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:
9324          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9325          * @param {Function} fn The function to call
9326          * @param {Object} scope (optional) The scope of the function
9327          * @return {Roo.KeyMap} The KeyMap created
9328          */
9329         addKeyListener : function(key, fn, scope){
9330             var config;
9331             if(typeof key != "object" || key instanceof Array){
9332                 config = {
9333                     key: key,
9334                     fn: fn,
9335                     scope: scope
9336                 };
9337             }else{
9338                 config = {
9339                     key : key.key,
9340                     shift : key.shift,
9341                     ctrl : key.ctrl,
9342                     alt : key.alt,
9343                     fn: fn,
9344                     scope: scope
9345                 };
9346             }
9347             return new Roo.KeyMap(this, config);
9348         },
9349
9350         /**
9351          * Creates a KeyMap for this element
9352          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9353          * @return {Roo.KeyMap} The KeyMap created
9354          */
9355         addKeyMap : function(config){
9356             return new Roo.KeyMap(this, config);
9357         },
9358
9359         /**
9360          * Returns true if this element is scrollable.
9361          * @return {Boolean}
9362          */
9363          isScrollable : function(){
9364             var dom = this.dom;
9365             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9366         },
9367
9368         /**
9369          * 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().
9370          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9371          * @param {Number} value The new scroll value
9372          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9373          * @return {Element} this
9374          */
9375
9376         scrollTo : function(side, value, animate){
9377             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9378             if(!animate || !A){
9379                 this.dom[prop] = value;
9380             }else{
9381                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9382                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9383             }
9384             return this;
9385         },
9386
9387         /**
9388          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9389          * within this element's scrollable range.
9390          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9391          * @param {Number} distance How far to scroll the element in pixels
9392          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9393          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9394          * was scrolled as far as it could go.
9395          */
9396          scroll : function(direction, distance, animate){
9397              if(!this.isScrollable()){
9398                  return;
9399              }
9400              var el = this.dom;
9401              var l = el.scrollLeft, t = el.scrollTop;
9402              var w = el.scrollWidth, h = el.scrollHeight;
9403              var cw = el.clientWidth, ch = el.clientHeight;
9404              direction = direction.toLowerCase();
9405              var scrolled = false;
9406              var a = this.preanim(arguments, 2);
9407              switch(direction){
9408                  case "l":
9409                  case "left":
9410                      if(w - l > cw){
9411                          var v = Math.min(l + distance, w-cw);
9412                          this.scrollTo("left", v, a);
9413                          scrolled = true;
9414                      }
9415                      break;
9416                 case "r":
9417                 case "right":
9418                      if(l > 0){
9419                          var v = Math.max(l - distance, 0);
9420                          this.scrollTo("left", v, a);
9421                          scrolled = true;
9422                      }
9423                      break;
9424                 case "t":
9425                 case "top":
9426                 case "up":
9427                      if(t > 0){
9428                          var v = Math.max(t - distance, 0);
9429                          this.scrollTo("top", v, a);
9430                          scrolled = true;
9431                      }
9432                      break;
9433                 case "b":
9434                 case "bottom":
9435                 case "down":
9436                      if(h - t > ch){
9437                          var v = Math.min(t + distance, h-ch);
9438                          this.scrollTo("top", v, a);
9439                          scrolled = true;
9440                      }
9441                      break;
9442              }
9443              return scrolled;
9444         },
9445
9446         /**
9447          * Translates the passed page coordinates into left/top css values for this element
9448          * @param {Number/Array} x The page x or an array containing [x, y]
9449          * @param {Number} y The page y
9450          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9451          */
9452         translatePoints : function(x, y){
9453             if(typeof x == 'object' || x instanceof Array){
9454                 y = x[1]; x = x[0];
9455             }
9456             var p = this.getStyle('position');
9457             var o = this.getXY();
9458
9459             var l = parseInt(this.getStyle('left'), 10);
9460             var t = parseInt(this.getStyle('top'), 10);
9461
9462             if(isNaN(l)){
9463                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9464             }
9465             if(isNaN(t)){
9466                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9467             }
9468
9469             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9470         },
9471
9472         /**
9473          * Returns the current scroll position of the element.
9474          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9475          */
9476         getScroll : function(){
9477             var d = this.dom, doc = document;
9478             if(d == doc || d == doc.body){
9479                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9480                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9481                 return {left: l, top: t};
9482             }else{
9483                 return {left: d.scrollLeft, top: d.scrollTop};
9484             }
9485         },
9486
9487         /**
9488          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9489          * are convert to standard 6 digit hex color.
9490          * @param {String} attr The css attribute
9491          * @param {String} defaultValue The default value to use when a valid color isn't found
9492          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9493          * YUI color anims.
9494          */
9495         getColor : function(attr, defaultValue, prefix){
9496             var v = this.getStyle(attr);
9497             if(!v || v == "transparent" || v == "inherit") {
9498                 return defaultValue;
9499             }
9500             var color = typeof prefix == "undefined" ? "#" : prefix;
9501             if(v.substr(0, 4) == "rgb("){
9502                 var rvs = v.slice(4, v.length -1).split(",");
9503                 for(var i = 0; i < 3; i++){
9504                     var h = parseInt(rvs[i]).toString(16);
9505                     if(h < 16){
9506                         h = "0" + h;
9507                     }
9508                     color += h;
9509                 }
9510             } else {
9511                 if(v.substr(0, 1) == "#"){
9512                     if(v.length == 4) {
9513                         for(var i = 1; i < 4; i++){
9514                             var c = v.charAt(i);
9515                             color +=  c + c;
9516                         }
9517                     }else if(v.length == 7){
9518                         color += v.substr(1);
9519                     }
9520                 }
9521             }
9522             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9523         },
9524
9525         /**
9526          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9527          * gradient background, rounded corners and a 4-way shadow.
9528          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9529          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9530          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9531          * @return {Roo.Element} this
9532          */
9533         boxWrap : function(cls){
9534             cls = cls || 'x-box';
9535             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9536             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9537             return el;
9538         },
9539
9540         /**
9541          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9542          * @param {String} namespace The namespace in which to look for the attribute
9543          * @param {String} name The attribute name
9544          * @return {String} The attribute value
9545          */
9546         getAttributeNS : Roo.isIE ? function(ns, name){
9547             var d = this.dom;
9548             var type = typeof d[ns+":"+name];
9549             if(type != 'undefined' && type != 'unknown'){
9550                 return d[ns+":"+name];
9551             }
9552             return d[name];
9553         } : function(ns, name){
9554             var d = this.dom;
9555             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9556         }
9557     };
9558
9559     var ep = El.prototype;
9560
9561     /**
9562      * Appends an event handler (Shorthand for addListener)
9563      * @param {String}   eventName     The type of event to append
9564      * @param {Function} fn        The method the event invokes
9565      * @param {Object} scope       (optional) The scope (this object) of the fn
9566      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9567      * @method
9568      */
9569     ep.on = ep.addListener;
9570         // backwards compat
9571     ep.mon = ep.addListener;
9572
9573     /**
9574      * Removes an event handler from this element (shorthand for removeListener)
9575      * @param {String} eventName the type of event to remove
9576      * @param {Function} fn the method the event invokes
9577      * @return {Roo.Element} this
9578      * @method
9579      */
9580     ep.un = ep.removeListener;
9581
9582     /**
9583      * true to automatically adjust width and height settings for box-model issues (default to true)
9584      */
9585     ep.autoBoxAdjust = true;
9586
9587     // private
9588     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9589
9590     // private
9591     El.addUnits = function(v, defaultUnit){
9592         if(v === "" || v == "auto"){
9593             return v;
9594         }
9595         if(v === undefined){
9596             return '';
9597         }
9598         if(typeof v == "number" || !El.unitPattern.test(v)){
9599             return v + (defaultUnit || 'px');
9600         }
9601         return v;
9602     };
9603
9604     // special markup used throughout Roo when box wrapping elements
9605     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>';
9606     /**
9607      * Visibility mode constant - Use visibility to hide element
9608      * @static
9609      * @type Number
9610      */
9611     El.VISIBILITY = 1;
9612     /**
9613      * Visibility mode constant - Use display to hide element
9614      * @static
9615      * @type Number
9616      */
9617     El.DISPLAY = 2;
9618
9619     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9620     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9621     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9622
9623
9624
9625     /**
9626      * @private
9627      */
9628     El.cache = {};
9629
9630     var docEl;
9631
9632     /**
9633      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9634      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9635      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9636      * @return {Element} The Element object
9637      * @static
9638      */
9639     El.get = function(el){
9640         var ex, elm, id;
9641         if(!el){ return null; }
9642         if(typeof el == "string"){ // element id
9643             if(!(elm = document.getElementById(el))){
9644                 return null;
9645             }
9646             if(ex = El.cache[el]){
9647                 ex.dom = elm;
9648             }else{
9649                 ex = El.cache[el] = new El(elm);
9650             }
9651             return ex;
9652         }else if(el.tagName){ // dom element
9653             if(!(id = el.id)){
9654                 id = Roo.id(el);
9655             }
9656             if(ex = El.cache[id]){
9657                 ex.dom = el;
9658             }else{
9659                 ex = El.cache[id] = new El(el);
9660             }
9661             return ex;
9662         }else if(el instanceof El){
9663             if(el != docEl){
9664                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9665                                                               // catch case where it hasn't been appended
9666                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9667             }
9668             return el;
9669         }else if(el.isComposite){
9670             return el;
9671         }else if(el instanceof Array){
9672             return El.select(el);
9673         }else if(el == document){
9674             // create a bogus element object representing the document object
9675             if(!docEl){
9676                 var f = function(){};
9677                 f.prototype = El.prototype;
9678                 docEl = new f();
9679                 docEl.dom = document;
9680             }
9681             return docEl;
9682         }
9683         return null;
9684     };
9685
9686     // private
9687     El.uncache = function(el){
9688         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9689             if(a[i]){
9690                 delete El.cache[a[i].id || a[i]];
9691             }
9692         }
9693     };
9694
9695     // private
9696     // Garbage collection - uncache elements/purge listeners on orphaned elements
9697     // so we don't hold a reference and cause the browser to retain them
9698     El.garbageCollect = function(){
9699         if(!Roo.enableGarbageCollector){
9700             clearInterval(El.collectorThread);
9701             return;
9702         }
9703         for(var eid in El.cache){
9704             var el = El.cache[eid], d = el.dom;
9705             // -------------------------------------------------------
9706             // Determining what is garbage:
9707             // -------------------------------------------------------
9708             // !d
9709             // dom node is null, definitely garbage
9710             // -------------------------------------------------------
9711             // !d.parentNode
9712             // no parentNode == direct orphan, definitely garbage
9713             // -------------------------------------------------------
9714             // !d.offsetParent && !document.getElementById(eid)
9715             // display none elements have no offsetParent so we will
9716             // also try to look it up by it's id. However, check
9717             // offsetParent first so we don't do unneeded lookups.
9718             // This enables collection of elements that are not orphans
9719             // directly, but somewhere up the line they have an orphan
9720             // parent.
9721             // -------------------------------------------------------
9722             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9723                 delete El.cache[eid];
9724                 if(d && Roo.enableListenerCollection){
9725                     E.purgeElement(d);
9726                 }
9727             }
9728         }
9729     }
9730     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9731
9732
9733     // dom is optional
9734     El.Flyweight = function(dom){
9735         this.dom = dom;
9736     };
9737     El.Flyweight.prototype = El.prototype;
9738
9739     El._flyweights = {};
9740     /**
9741      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9742      * the dom node can be overwritten by other code.
9743      * @param {String/HTMLElement} el The dom node or id
9744      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9745      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9746      * @static
9747      * @return {Element} The shared Element object
9748      */
9749     El.fly = function(el, named){
9750         named = named || '_global';
9751         el = Roo.getDom(el);
9752         if(!el){
9753             return null;
9754         }
9755         if(!El._flyweights[named]){
9756             El._flyweights[named] = new El.Flyweight();
9757         }
9758         El._flyweights[named].dom = el;
9759         return El._flyweights[named];
9760     };
9761
9762     /**
9763      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9764      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9765      * Shorthand of {@link Roo.Element#get}
9766      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9767      * @return {Element} The Element object
9768      * @member Roo
9769      * @method get
9770      */
9771     Roo.get = El.get;
9772     /**
9773      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9774      * the dom node can be overwritten by other code.
9775      * Shorthand of {@link Roo.Element#fly}
9776      * @param {String/HTMLElement} el The dom node or id
9777      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9778      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9779      * @static
9780      * @return {Element} The shared Element object
9781      * @member Roo
9782      * @method fly
9783      */
9784     Roo.fly = El.fly;
9785
9786     // speedy lookup for elements never to box adjust
9787     var noBoxAdjust = Roo.isStrict ? {
9788         select:1
9789     } : {
9790         input:1, select:1, textarea:1
9791     };
9792     if(Roo.isIE || Roo.isGecko){
9793         noBoxAdjust['button'] = 1;
9794     }
9795
9796
9797     Roo.EventManager.on(window, 'unload', function(){
9798         delete El.cache;
9799         delete El._flyweights;
9800     });
9801 })();
9802
9803
9804
9805
9806 if(Roo.DomQuery){
9807     Roo.Element.selectorFunction = Roo.DomQuery.select;
9808 }
9809
9810 Roo.Element.select = function(selector, unique, root){
9811     var els;
9812     if(typeof selector == "string"){
9813         els = Roo.Element.selectorFunction(selector, root);
9814     }else if(selector.length !== undefined){
9815         els = selector;
9816     }else{
9817         throw "Invalid selector";
9818     }
9819     if(unique === true){
9820         return new Roo.CompositeElement(els);
9821     }else{
9822         return new Roo.CompositeElementLite(els);
9823     }
9824 };
9825 /**
9826  * Selects elements based on the passed CSS selector to enable working on them as 1.
9827  * @param {String/Array} selector The CSS selector or an array of elements
9828  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9829  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9830  * @return {CompositeElementLite/CompositeElement}
9831  * @member Roo
9832  * @method select
9833  */
9834 Roo.select = Roo.Element.select;
9835
9836
9837
9838
9839
9840
9841
9842
9843
9844
9845
9846
9847
9848
9849 /*
9850  * Based on:
9851  * Ext JS Library 1.1.1
9852  * Copyright(c) 2006-2007, Ext JS, LLC.
9853  *
9854  * Originally Released Under LGPL - original licence link has changed is not relivant.
9855  *
9856  * Fork - LGPL
9857  * <script type="text/javascript">
9858  */
9859
9860
9861
9862 //Notifies Element that fx methods are available
9863 Roo.enableFx = true;
9864
9865 /**
9866  * @class Roo.Fx
9867  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9868  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9869  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9870  * Element effects to work.</p><br/>
9871  *
9872  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9873  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9874  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9875  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9876  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9877  * expected results and should be done with care.</p><br/>
9878  *
9879  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9880  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9881 <pre>
9882 Value  Description
9883 -----  -----------------------------
9884 tl     The top left corner
9885 t      The center of the top edge
9886 tr     The top right corner
9887 l      The center of the left edge
9888 r      The center of the right edge
9889 bl     The bottom left corner
9890 b      The center of the bottom edge
9891 br     The bottom right corner
9892 </pre>
9893  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9894  * below are common options that can be passed to any Fx method.</b>
9895  * @cfg {Function} callback A function called when the effect is finished
9896  * @cfg {Object} scope The scope of the effect function
9897  * @cfg {String} easing A valid Easing value for the effect
9898  * @cfg {String} afterCls A css class to apply after the effect
9899  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9900  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9901  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9902  * effects that end with the element being visually hidden, ignored otherwise)
9903  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9904  * a function which returns such a specification that will be applied to the Element after the effect finishes
9905  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9906  * @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
9907  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9908  */
9909 Roo.Fx = {
9910         /**
9911          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9912          * origin for the slide effect.  This function automatically handles wrapping the element with
9913          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9914          * Usage:
9915          *<pre><code>
9916 // default: slide the element in from the top
9917 el.slideIn();
9918
9919 // custom: slide the element in from the right with a 2-second duration
9920 el.slideIn('r', { duration: 2 });
9921
9922 // common config options shown with default values
9923 el.slideIn('t', {
9924     easing: 'easeOut',
9925     duration: .5
9926 });
9927 </code></pre>
9928          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9929          * @param {Object} options (optional) Object literal with any of the Fx config options
9930          * @return {Roo.Element} The Element
9931          */
9932     slideIn : function(anchor, o){
9933         var el = this.getFxEl();
9934         o = o || {};
9935
9936         el.queueFx(o, function(){
9937
9938             anchor = anchor || "t";
9939
9940             // fix display to visibility
9941             this.fixDisplay();
9942
9943             // restore values after effect
9944             var r = this.getFxRestore();
9945             var b = this.getBox();
9946             // fixed size for slide
9947             this.setSize(b);
9948
9949             // wrap if needed
9950             var wrap = this.fxWrap(r.pos, o, "hidden");
9951
9952             var st = this.dom.style;
9953             st.visibility = "visible";
9954             st.position = "absolute";
9955
9956             // clear out temp styles after slide and unwrap
9957             var after = function(){
9958                 el.fxUnwrap(wrap, r.pos, o);
9959                 st.width = r.width;
9960                 st.height = r.height;
9961                 el.afterFx(o);
9962             };
9963             // time to calc the positions
9964             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9965
9966             switch(anchor.toLowerCase()){
9967                 case "t":
9968                     wrap.setSize(b.width, 0);
9969                     st.left = st.bottom = "0";
9970                     a = {height: bh};
9971                 break;
9972                 case "l":
9973                     wrap.setSize(0, b.height);
9974                     st.right = st.top = "0";
9975                     a = {width: bw};
9976                 break;
9977                 case "r":
9978                     wrap.setSize(0, b.height);
9979                     wrap.setX(b.right);
9980                     st.left = st.top = "0";
9981                     a = {width: bw, points: pt};
9982                 break;
9983                 case "b":
9984                     wrap.setSize(b.width, 0);
9985                     wrap.setY(b.bottom);
9986                     st.left = st.top = "0";
9987                     a = {height: bh, points: pt};
9988                 break;
9989                 case "tl":
9990                     wrap.setSize(0, 0);
9991                     st.right = st.bottom = "0";
9992                     a = {width: bw, height: bh};
9993                 break;
9994                 case "bl":
9995                     wrap.setSize(0, 0);
9996                     wrap.setY(b.y+b.height);
9997                     st.right = st.top = "0";
9998                     a = {width: bw, height: bh, points: pt};
9999                 break;
10000                 case "br":
10001                     wrap.setSize(0, 0);
10002                     wrap.setXY([b.right, b.bottom]);
10003                     st.left = st.top = "0";
10004                     a = {width: bw, height: bh, points: pt};
10005                 break;
10006                 case "tr":
10007                     wrap.setSize(0, 0);
10008                     wrap.setX(b.x+b.width);
10009                     st.left = st.bottom = "0";
10010                     a = {width: bw, height: bh, points: pt};
10011                 break;
10012             }
10013             this.dom.style.visibility = "visible";
10014             wrap.show();
10015
10016             arguments.callee.anim = wrap.fxanim(a,
10017                 o,
10018                 'motion',
10019                 .5,
10020                 'easeOut', after);
10021         });
10022         return this;
10023     },
10024     
10025         /**
10026          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10027          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10028          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10029          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10030          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10031          * Usage:
10032          *<pre><code>
10033 // default: slide the element out to the top
10034 el.slideOut();
10035
10036 // custom: slide the element out to the right with a 2-second duration
10037 el.slideOut('r', { duration: 2 });
10038
10039 // common config options shown with default values
10040 el.slideOut('t', {
10041     easing: 'easeOut',
10042     duration: .5,
10043     remove: false,
10044     useDisplay: false
10045 });
10046 </code></pre>
10047          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10048          * @param {Object} options (optional) Object literal with any of the Fx config options
10049          * @return {Roo.Element} The Element
10050          */
10051     slideOut : function(anchor, o){
10052         var el = this.getFxEl();
10053         o = o || {};
10054
10055         el.queueFx(o, function(){
10056
10057             anchor = anchor || "t";
10058
10059             // restore values after effect
10060             var r = this.getFxRestore();
10061             
10062             var b = this.getBox();
10063             // fixed size for slide
10064             this.setSize(b);
10065
10066             // wrap if needed
10067             var wrap = this.fxWrap(r.pos, o, "visible");
10068
10069             var st = this.dom.style;
10070             st.visibility = "visible";
10071             st.position = "absolute";
10072
10073             wrap.setSize(b);
10074
10075             var after = function(){
10076                 if(o.useDisplay){
10077                     el.setDisplayed(false);
10078                 }else{
10079                     el.hide();
10080                 }
10081
10082                 el.fxUnwrap(wrap, r.pos, o);
10083
10084                 st.width = r.width;
10085                 st.height = r.height;
10086
10087                 el.afterFx(o);
10088             };
10089
10090             var a, zero = {to: 0};
10091             switch(anchor.toLowerCase()){
10092                 case "t":
10093                     st.left = st.bottom = "0";
10094                     a = {height: zero};
10095                 break;
10096                 case "l":
10097                     st.right = st.top = "0";
10098                     a = {width: zero};
10099                 break;
10100                 case "r":
10101                     st.left = st.top = "0";
10102                     a = {width: zero, points: {to:[b.right, b.y]}};
10103                 break;
10104                 case "b":
10105                     st.left = st.top = "0";
10106                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10107                 break;
10108                 case "tl":
10109                     st.right = st.bottom = "0";
10110                     a = {width: zero, height: zero};
10111                 break;
10112                 case "bl":
10113                     st.right = st.top = "0";
10114                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10115                 break;
10116                 case "br":
10117                     st.left = st.top = "0";
10118                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10119                 break;
10120                 case "tr":
10121                     st.left = st.bottom = "0";
10122                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10123                 break;
10124             }
10125
10126             arguments.callee.anim = wrap.fxanim(a,
10127                 o,
10128                 'motion',
10129                 .5,
10130                 "easeOut", after);
10131         });
10132         return this;
10133     },
10134
10135         /**
10136          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10137          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10138          * The element must be removed from the DOM using the 'remove' config option if desired.
10139          * Usage:
10140          *<pre><code>
10141 // default
10142 el.puff();
10143
10144 // common config options shown with default values
10145 el.puff({
10146     easing: 'easeOut',
10147     duration: .5,
10148     remove: false,
10149     useDisplay: false
10150 });
10151 </code></pre>
10152          * @param {Object} options (optional) Object literal with any of the Fx config options
10153          * @return {Roo.Element} The Element
10154          */
10155     puff : function(o){
10156         var el = this.getFxEl();
10157         o = o || {};
10158
10159         el.queueFx(o, function(){
10160             this.clearOpacity();
10161             this.show();
10162
10163             // restore values after effect
10164             var r = this.getFxRestore();
10165             var st = this.dom.style;
10166
10167             var after = function(){
10168                 if(o.useDisplay){
10169                     el.setDisplayed(false);
10170                 }else{
10171                     el.hide();
10172                 }
10173
10174                 el.clearOpacity();
10175
10176                 el.setPositioning(r.pos);
10177                 st.width = r.width;
10178                 st.height = r.height;
10179                 st.fontSize = '';
10180                 el.afterFx(o);
10181             };
10182
10183             var width = this.getWidth();
10184             var height = this.getHeight();
10185
10186             arguments.callee.anim = this.fxanim({
10187                     width : {to: this.adjustWidth(width * 2)},
10188                     height : {to: this.adjustHeight(height * 2)},
10189                     points : {by: [-(width * .5), -(height * .5)]},
10190                     opacity : {to: 0},
10191                     fontSize: {to:200, unit: "%"}
10192                 },
10193                 o,
10194                 'motion',
10195                 .5,
10196                 "easeOut", after);
10197         });
10198         return this;
10199     },
10200
10201         /**
10202          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10203          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10204          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10205          * Usage:
10206          *<pre><code>
10207 // default
10208 el.switchOff();
10209
10210 // all config options shown with default values
10211 el.switchOff({
10212     easing: 'easeIn',
10213     duration: .3,
10214     remove: false,
10215     useDisplay: false
10216 });
10217 </code></pre>
10218          * @param {Object} options (optional) Object literal with any of the Fx config options
10219          * @return {Roo.Element} The Element
10220          */
10221     switchOff : function(o){
10222         var el = this.getFxEl();
10223         o = o || {};
10224
10225         el.queueFx(o, function(){
10226             this.clearOpacity();
10227             this.clip();
10228
10229             // restore values after effect
10230             var r = this.getFxRestore();
10231             var st = this.dom.style;
10232
10233             var after = function(){
10234                 if(o.useDisplay){
10235                     el.setDisplayed(false);
10236                 }else{
10237                     el.hide();
10238                 }
10239
10240                 el.clearOpacity();
10241                 el.setPositioning(r.pos);
10242                 st.width = r.width;
10243                 st.height = r.height;
10244
10245                 el.afterFx(o);
10246             };
10247
10248             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10249                 this.clearOpacity();
10250                 (function(){
10251                     this.fxanim({
10252                         height:{to:1},
10253                         points:{by:[0, this.getHeight() * .5]}
10254                     }, o, 'motion', 0.3, 'easeIn', after);
10255                 }).defer(100, this);
10256             });
10257         });
10258         return this;
10259     },
10260
10261     /**
10262      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10263      * changed using the "attr" config option) and then fading back to the original color. If no original
10264      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10265      * Usage:
10266 <pre><code>
10267 // default: highlight background to yellow
10268 el.highlight();
10269
10270 // custom: highlight foreground text to blue for 2 seconds
10271 el.highlight("0000ff", { attr: 'color', duration: 2 });
10272
10273 // common config options shown with default values
10274 el.highlight("ffff9c", {
10275     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10276     endColor: (current color) or "ffffff",
10277     easing: 'easeIn',
10278     duration: 1
10279 });
10280 </code></pre>
10281      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10282      * @param {Object} options (optional) Object literal with any of the Fx config options
10283      * @return {Roo.Element} The Element
10284      */ 
10285     highlight : function(color, o){
10286         var el = this.getFxEl();
10287         o = o || {};
10288
10289         el.queueFx(o, function(){
10290             color = color || "ffff9c";
10291             attr = o.attr || "backgroundColor";
10292
10293             this.clearOpacity();
10294             this.show();
10295
10296             var origColor = this.getColor(attr);
10297             var restoreColor = this.dom.style[attr];
10298             endColor = (o.endColor || origColor) || "ffffff";
10299
10300             var after = function(){
10301                 el.dom.style[attr] = restoreColor;
10302                 el.afterFx(o);
10303             };
10304
10305             var a = {};
10306             a[attr] = {from: color, to: endColor};
10307             arguments.callee.anim = this.fxanim(a,
10308                 o,
10309                 'color',
10310                 1,
10311                 'easeIn', after);
10312         });
10313         return this;
10314     },
10315
10316    /**
10317     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10318     * Usage:
10319 <pre><code>
10320 // default: a single light blue ripple
10321 el.frame();
10322
10323 // custom: 3 red ripples lasting 3 seconds total
10324 el.frame("ff0000", 3, { duration: 3 });
10325
10326 // common config options shown with default values
10327 el.frame("C3DAF9", 1, {
10328     duration: 1 //duration of entire animation (not each individual ripple)
10329     // Note: Easing is not configurable and will be ignored if included
10330 });
10331 </code></pre>
10332     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10333     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10334     * @param {Object} options (optional) Object literal with any of the Fx config options
10335     * @return {Roo.Element} The Element
10336     */
10337     frame : function(color, count, o){
10338         var el = this.getFxEl();
10339         o = o || {};
10340
10341         el.queueFx(o, function(){
10342             color = color || "#C3DAF9";
10343             if(color.length == 6){
10344                 color = "#" + color;
10345             }
10346             count = count || 1;
10347             duration = o.duration || 1;
10348             this.show();
10349
10350             var b = this.getBox();
10351             var animFn = function(){
10352                 var proxy = this.createProxy({
10353
10354                      style:{
10355                         visbility:"hidden",
10356                         position:"absolute",
10357                         "z-index":"35000", // yee haw
10358                         border:"0px solid " + color
10359                      }
10360                   });
10361                 var scale = Roo.isBorderBox ? 2 : 1;
10362                 proxy.animate({
10363                     top:{from:b.y, to:b.y - 20},
10364                     left:{from:b.x, to:b.x - 20},
10365                     borderWidth:{from:0, to:10},
10366                     opacity:{from:1, to:0},
10367                     height:{from:b.height, to:(b.height + (20*scale))},
10368                     width:{from:b.width, to:(b.width + (20*scale))}
10369                 }, duration, function(){
10370                     proxy.remove();
10371                 });
10372                 if(--count > 0){
10373                      animFn.defer((duration/2)*1000, this);
10374                 }else{
10375                     el.afterFx(o);
10376                 }
10377             };
10378             animFn.call(this);
10379         });
10380         return this;
10381     },
10382
10383    /**
10384     * Creates a pause before any subsequent queued effects begin.  If there are
10385     * no effects queued after the pause it will have no effect.
10386     * Usage:
10387 <pre><code>
10388 el.pause(1);
10389 </code></pre>
10390     * @param {Number} seconds The length of time to pause (in seconds)
10391     * @return {Roo.Element} The Element
10392     */
10393     pause : function(seconds){
10394         var el = this.getFxEl();
10395         var o = {};
10396
10397         el.queueFx(o, function(){
10398             setTimeout(function(){
10399                 el.afterFx(o);
10400             }, seconds * 1000);
10401         });
10402         return this;
10403     },
10404
10405    /**
10406     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10407     * using the "endOpacity" config option.
10408     * Usage:
10409 <pre><code>
10410 // default: fade in from opacity 0 to 100%
10411 el.fadeIn();
10412
10413 // custom: fade in from opacity 0 to 75% over 2 seconds
10414 el.fadeIn({ endOpacity: .75, duration: 2});
10415
10416 // common config options shown with default values
10417 el.fadeIn({
10418     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10419     easing: 'easeOut',
10420     duration: .5
10421 });
10422 </code></pre>
10423     * @param {Object} options (optional) Object literal with any of the Fx config options
10424     * @return {Roo.Element} The Element
10425     */
10426     fadeIn : function(o){
10427         var el = this.getFxEl();
10428         o = o || {};
10429         el.queueFx(o, function(){
10430             this.setOpacity(0);
10431             this.fixDisplay();
10432             this.dom.style.visibility = 'visible';
10433             var to = o.endOpacity || 1;
10434             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10435                 o, null, .5, "easeOut", function(){
10436                 if(to == 1){
10437                     this.clearOpacity();
10438                 }
10439                 el.afterFx(o);
10440             });
10441         });
10442         return this;
10443     },
10444
10445    /**
10446     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10447     * using the "endOpacity" config option.
10448     * Usage:
10449 <pre><code>
10450 // default: fade out from the element's current opacity to 0
10451 el.fadeOut();
10452
10453 // custom: fade out from the element's current opacity to 25% over 2 seconds
10454 el.fadeOut({ endOpacity: .25, duration: 2});
10455
10456 // common config options shown with default values
10457 el.fadeOut({
10458     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10459     easing: 'easeOut',
10460     duration: .5
10461     remove: false,
10462     useDisplay: false
10463 });
10464 </code></pre>
10465     * @param {Object} options (optional) Object literal with any of the Fx config options
10466     * @return {Roo.Element} The Element
10467     */
10468     fadeOut : function(o){
10469         var el = this.getFxEl();
10470         o = o || {};
10471         el.queueFx(o, function(){
10472             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10473                 o, null, .5, "easeOut", function(){
10474                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10475                      this.dom.style.display = "none";
10476                 }else{
10477                      this.dom.style.visibility = "hidden";
10478                 }
10479                 this.clearOpacity();
10480                 el.afterFx(o);
10481             });
10482         });
10483         return this;
10484     },
10485
10486    /**
10487     * Animates the transition of an element's dimensions from a starting height/width
10488     * to an ending height/width.
10489     * Usage:
10490 <pre><code>
10491 // change height and width to 100x100 pixels
10492 el.scale(100, 100);
10493
10494 // common config options shown with default values.  The height and width will default to
10495 // the element's existing values if passed as null.
10496 el.scale(
10497     [element's width],
10498     [element's height], {
10499     easing: 'easeOut',
10500     duration: .35
10501 });
10502 </code></pre>
10503     * @param {Number} width  The new width (pass undefined to keep the original width)
10504     * @param {Number} height  The new height (pass undefined to keep the original height)
10505     * @param {Object} options (optional) Object literal with any of the Fx config options
10506     * @return {Roo.Element} The Element
10507     */
10508     scale : function(w, h, o){
10509         this.shift(Roo.apply({}, o, {
10510             width: w,
10511             height: h
10512         }));
10513         return this;
10514     },
10515
10516    /**
10517     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10518     * Any of these properties not specified in the config object will not be changed.  This effect 
10519     * requires that at least one new dimension, position or opacity setting must be passed in on
10520     * the config object in order for the function to have any effect.
10521     * Usage:
10522 <pre><code>
10523 // slide the element horizontally to x position 200 while changing the height and opacity
10524 el.shift({ x: 200, height: 50, opacity: .8 });
10525
10526 // common config options shown with default values.
10527 el.shift({
10528     width: [element's width],
10529     height: [element's height],
10530     x: [element's x position],
10531     y: [element's y position],
10532     opacity: [element's opacity],
10533     easing: 'easeOut',
10534     duration: .35
10535 });
10536 </code></pre>
10537     * @param {Object} options  Object literal with any of the Fx config options
10538     * @return {Roo.Element} The Element
10539     */
10540     shift : function(o){
10541         var el = this.getFxEl();
10542         o = o || {};
10543         el.queueFx(o, function(){
10544             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10545             if(w !== undefined){
10546                 a.width = {to: this.adjustWidth(w)};
10547             }
10548             if(h !== undefined){
10549                 a.height = {to: this.adjustHeight(h)};
10550             }
10551             if(x !== undefined || y !== undefined){
10552                 a.points = {to: [
10553                     x !== undefined ? x : this.getX(),
10554                     y !== undefined ? y : this.getY()
10555                 ]};
10556             }
10557             if(op !== undefined){
10558                 a.opacity = {to: op};
10559             }
10560             if(o.xy !== undefined){
10561                 a.points = {to: o.xy};
10562             }
10563             arguments.callee.anim = this.fxanim(a,
10564                 o, 'motion', .35, "easeOut", function(){
10565                 el.afterFx(o);
10566             });
10567         });
10568         return this;
10569     },
10570
10571         /**
10572          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10573          * ending point of the effect.
10574          * Usage:
10575          *<pre><code>
10576 // default: slide the element downward while fading out
10577 el.ghost();
10578
10579 // custom: slide the element out to the right with a 2-second duration
10580 el.ghost('r', { duration: 2 });
10581
10582 // common config options shown with default values
10583 el.ghost('b', {
10584     easing: 'easeOut',
10585     duration: .5
10586     remove: false,
10587     useDisplay: false
10588 });
10589 </code></pre>
10590          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10591          * @param {Object} options (optional) Object literal with any of the Fx config options
10592          * @return {Roo.Element} The Element
10593          */
10594     ghost : function(anchor, o){
10595         var el = this.getFxEl();
10596         o = o || {};
10597
10598         el.queueFx(o, function(){
10599             anchor = anchor || "b";
10600
10601             // restore values after effect
10602             var r = this.getFxRestore();
10603             var w = this.getWidth(),
10604                 h = this.getHeight();
10605
10606             var st = this.dom.style;
10607
10608             var after = function(){
10609                 if(o.useDisplay){
10610                     el.setDisplayed(false);
10611                 }else{
10612                     el.hide();
10613                 }
10614
10615                 el.clearOpacity();
10616                 el.setPositioning(r.pos);
10617                 st.width = r.width;
10618                 st.height = r.height;
10619
10620                 el.afterFx(o);
10621             };
10622
10623             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10624             switch(anchor.toLowerCase()){
10625                 case "t":
10626                     pt.by = [0, -h];
10627                 break;
10628                 case "l":
10629                     pt.by = [-w, 0];
10630                 break;
10631                 case "r":
10632                     pt.by = [w, 0];
10633                 break;
10634                 case "b":
10635                     pt.by = [0, h];
10636                 break;
10637                 case "tl":
10638                     pt.by = [-w, -h];
10639                 break;
10640                 case "bl":
10641                     pt.by = [-w, h];
10642                 break;
10643                 case "br":
10644                     pt.by = [w, h];
10645                 break;
10646                 case "tr":
10647                     pt.by = [w, -h];
10648                 break;
10649             }
10650
10651             arguments.callee.anim = this.fxanim(a,
10652                 o,
10653                 'motion',
10654                 .5,
10655                 "easeOut", after);
10656         });
10657         return this;
10658     },
10659
10660         /**
10661          * Ensures that all effects queued after syncFx is called on the element are
10662          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10663          * @return {Roo.Element} The Element
10664          */
10665     syncFx : function(){
10666         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10667             block : false,
10668             concurrent : true,
10669             stopFx : false
10670         });
10671         return this;
10672     },
10673
10674         /**
10675          * Ensures that all effects queued after sequenceFx is called on the element are
10676          * run in sequence.  This is the opposite of {@link #syncFx}.
10677          * @return {Roo.Element} The Element
10678          */
10679     sequenceFx : function(){
10680         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10681             block : false,
10682             concurrent : false,
10683             stopFx : false
10684         });
10685         return this;
10686     },
10687
10688         /* @private */
10689     nextFx : function(){
10690         var ef = this.fxQueue[0];
10691         if(ef){
10692             ef.call(this);
10693         }
10694     },
10695
10696         /**
10697          * Returns true if the element has any effects actively running or queued, else returns false.
10698          * @return {Boolean} True if element has active effects, else false
10699          */
10700     hasActiveFx : function(){
10701         return this.fxQueue && this.fxQueue[0];
10702     },
10703
10704         /**
10705          * Stops any running effects and clears the element's internal effects queue if it contains
10706          * any additional effects that haven't started yet.
10707          * @return {Roo.Element} The Element
10708          */
10709     stopFx : function(){
10710         if(this.hasActiveFx()){
10711             var cur = this.fxQueue[0];
10712             if(cur && cur.anim && cur.anim.isAnimated()){
10713                 this.fxQueue = [cur]; // clear out others
10714                 cur.anim.stop(true);
10715             }
10716         }
10717         return this;
10718     },
10719
10720         /* @private */
10721     beforeFx : function(o){
10722         if(this.hasActiveFx() && !o.concurrent){
10723            if(o.stopFx){
10724                this.stopFx();
10725                return true;
10726            }
10727            return false;
10728         }
10729         return true;
10730     },
10731
10732         /**
10733          * Returns true if the element is currently blocking so that no other effect can be queued
10734          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10735          * used to ensure that an effect initiated by a user action runs to completion prior to the
10736          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10737          * @return {Boolean} True if blocking, else false
10738          */
10739     hasFxBlock : function(){
10740         var q = this.fxQueue;
10741         return q && q[0] && q[0].block;
10742     },
10743
10744         /* @private */
10745     queueFx : function(o, fn){
10746         if(!this.fxQueue){
10747             this.fxQueue = [];
10748         }
10749         if(!this.hasFxBlock()){
10750             Roo.applyIf(o, this.fxDefaults);
10751             if(!o.concurrent){
10752                 var run = this.beforeFx(o);
10753                 fn.block = o.block;
10754                 this.fxQueue.push(fn);
10755                 if(run){
10756                     this.nextFx();
10757                 }
10758             }else{
10759                 fn.call(this);
10760             }
10761         }
10762         return this;
10763     },
10764
10765         /* @private */
10766     fxWrap : function(pos, o, vis){
10767         var wrap;
10768         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10769             var wrapXY;
10770             if(o.fixPosition){
10771                 wrapXY = this.getXY();
10772             }
10773             var div = document.createElement("div");
10774             div.style.visibility = vis;
10775             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10776             wrap.setPositioning(pos);
10777             if(wrap.getStyle("position") == "static"){
10778                 wrap.position("relative");
10779             }
10780             this.clearPositioning('auto');
10781             wrap.clip();
10782             wrap.dom.appendChild(this.dom);
10783             if(wrapXY){
10784                 wrap.setXY(wrapXY);
10785             }
10786         }
10787         return wrap;
10788     },
10789
10790         /* @private */
10791     fxUnwrap : function(wrap, pos, o){
10792         this.clearPositioning();
10793         this.setPositioning(pos);
10794         if(!o.wrap){
10795             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10796             wrap.remove();
10797         }
10798     },
10799
10800         /* @private */
10801     getFxRestore : function(){
10802         var st = this.dom.style;
10803         return {pos: this.getPositioning(), width: st.width, height : st.height};
10804     },
10805
10806         /* @private */
10807     afterFx : function(o){
10808         if(o.afterStyle){
10809             this.applyStyles(o.afterStyle);
10810         }
10811         if(o.afterCls){
10812             this.addClass(o.afterCls);
10813         }
10814         if(o.remove === true){
10815             this.remove();
10816         }
10817         Roo.callback(o.callback, o.scope, [this]);
10818         if(!o.concurrent){
10819             this.fxQueue.shift();
10820             this.nextFx();
10821         }
10822     },
10823
10824         /* @private */
10825     getFxEl : function(){ // support for composite element fx
10826         return Roo.get(this.dom);
10827     },
10828
10829         /* @private */
10830     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10831         animType = animType || 'run';
10832         opt = opt || {};
10833         var anim = Roo.lib.Anim[animType](
10834             this.dom, args,
10835             (opt.duration || defaultDur) || .35,
10836             (opt.easing || defaultEase) || 'easeOut',
10837             function(){
10838                 Roo.callback(cb, this);
10839             },
10840             this
10841         );
10842         opt.anim = anim;
10843         return anim;
10844     }
10845 };
10846
10847 // backwords compat
10848 Roo.Fx.resize = Roo.Fx.scale;
10849
10850 //When included, Roo.Fx is automatically applied to Element so that all basic
10851 //effects are available directly via the Element API
10852 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10853  * Based on:
10854  * Ext JS Library 1.1.1
10855  * Copyright(c) 2006-2007, Ext JS, LLC.
10856  *
10857  * Originally Released Under LGPL - original licence link has changed is not relivant.
10858  *
10859  * Fork - LGPL
10860  * <script type="text/javascript">
10861  */
10862
10863
10864 /**
10865  * @class Roo.CompositeElement
10866  * Standard composite class. Creates a Roo.Element for every element in the collection.
10867  * <br><br>
10868  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10869  * actions will be performed on all the elements in this collection.</b>
10870  * <br><br>
10871  * All methods return <i>this</i> and can be chained.
10872  <pre><code>
10873  var els = Roo.select("#some-el div.some-class", true);
10874  // or select directly from an existing element
10875  var el = Roo.get('some-el');
10876  el.select('div.some-class', true);
10877
10878  els.setWidth(100); // all elements become 100 width
10879  els.hide(true); // all elements fade out and hide
10880  // or
10881  els.setWidth(100).hide(true);
10882  </code></pre>
10883  */
10884 Roo.CompositeElement = function(els){
10885     this.elements = [];
10886     this.addElements(els);
10887 };
10888 Roo.CompositeElement.prototype = {
10889     isComposite: true,
10890     addElements : function(els){
10891         if(!els) return this;
10892         if(typeof els == "string"){
10893             els = Roo.Element.selectorFunction(els);
10894         }
10895         var yels = this.elements;
10896         var index = yels.length-1;
10897         for(var i = 0, len = els.length; i < len; i++) {
10898                 yels[++index] = Roo.get(els[i]);
10899         }
10900         return this;
10901     },
10902
10903     /**
10904     * Clears this composite and adds the elements returned by the passed selector.
10905     * @param {String/Array} els A string CSS selector, an array of elements or an element
10906     * @return {CompositeElement} this
10907     */
10908     fill : function(els){
10909         this.elements = [];
10910         this.add(els);
10911         return this;
10912     },
10913
10914     /**
10915     * Filters this composite to only elements that match the passed selector.
10916     * @param {String} selector A string CSS selector
10917     * @return {CompositeElement} this
10918     */
10919     filter : function(selector){
10920         var els = [];
10921         this.each(function(el){
10922             if(el.is(selector)){
10923                 els[els.length] = el.dom;
10924             }
10925         });
10926         this.fill(els);
10927         return this;
10928     },
10929
10930     invoke : function(fn, args){
10931         var els = this.elements;
10932         for(var i = 0, len = els.length; i < len; i++) {
10933                 Roo.Element.prototype[fn].apply(els[i], args);
10934         }
10935         return this;
10936     },
10937     /**
10938     * Adds elements to this composite.
10939     * @param {String/Array} els A string CSS selector, an array of elements or an element
10940     * @return {CompositeElement} this
10941     */
10942     add : function(els){
10943         if(typeof els == "string"){
10944             this.addElements(Roo.Element.selectorFunction(els));
10945         }else if(els.length !== undefined){
10946             this.addElements(els);
10947         }else{
10948             this.addElements([els]);
10949         }
10950         return this;
10951     },
10952     /**
10953     * Calls the passed function passing (el, this, index) for each element in this composite.
10954     * @param {Function} fn The function to call
10955     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10956     * @return {CompositeElement} this
10957     */
10958     each : function(fn, scope){
10959         var els = this.elements;
10960         for(var i = 0, len = els.length; i < len; i++){
10961             if(fn.call(scope || els[i], els[i], this, i) === false) {
10962                 break;
10963             }
10964         }
10965         return this;
10966     },
10967
10968     /**
10969      * Returns the Element object at the specified index
10970      * @param {Number} index
10971      * @return {Roo.Element}
10972      */
10973     item : function(index){
10974         return this.elements[index] || null;
10975     },
10976
10977     /**
10978      * Returns the first Element
10979      * @return {Roo.Element}
10980      */
10981     first : function(){
10982         return this.item(0);
10983     },
10984
10985     /**
10986      * Returns the last Element
10987      * @return {Roo.Element}
10988      */
10989     last : function(){
10990         return this.item(this.elements.length-1);
10991     },
10992
10993     /**
10994      * Returns the number of elements in this composite
10995      * @return Number
10996      */
10997     getCount : function(){
10998         return this.elements.length;
10999     },
11000
11001     /**
11002      * Returns true if this composite contains the passed element
11003      * @return Boolean
11004      */
11005     contains : function(el){
11006         return this.indexOf(el) !== -1;
11007     },
11008
11009     /**
11010      * Returns true if this composite contains the passed element
11011      * @return Boolean
11012      */
11013     indexOf : function(el){
11014         return this.elements.indexOf(Roo.get(el));
11015     },
11016
11017
11018     /**
11019     * Removes the specified element(s).
11020     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11021     * or an array of any of those.
11022     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11023     * @return {CompositeElement} this
11024     */
11025     removeElement : function(el, removeDom){
11026         if(el instanceof Array){
11027             for(var i = 0, len = el.length; i < len; i++){
11028                 this.removeElement(el[i]);
11029             }
11030             return this;
11031         }
11032         var index = typeof el == 'number' ? el : this.indexOf(el);
11033         if(index !== -1){
11034             if(removeDom){
11035                 var d = this.elements[index];
11036                 if(d.dom){
11037                     d.remove();
11038                 }else{
11039                     d.parentNode.removeChild(d);
11040                 }
11041             }
11042             this.elements.splice(index, 1);
11043         }
11044         return this;
11045     },
11046
11047     /**
11048     * Replaces the specified element with the passed element.
11049     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11050     * to replace.
11051     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11052     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11053     * @return {CompositeElement} this
11054     */
11055     replaceElement : function(el, replacement, domReplace){
11056         var index = typeof el == 'number' ? el : this.indexOf(el);
11057         if(index !== -1){
11058             if(domReplace){
11059                 this.elements[index].replaceWith(replacement);
11060             }else{
11061                 this.elements.splice(index, 1, Roo.get(replacement))
11062             }
11063         }
11064         return this;
11065     },
11066
11067     /**
11068      * Removes all elements.
11069      */
11070     clear : function(){
11071         this.elements = [];
11072     }
11073 };
11074 (function(){
11075     Roo.CompositeElement.createCall = function(proto, fnName){
11076         if(!proto[fnName]){
11077             proto[fnName] = function(){
11078                 return this.invoke(fnName, arguments);
11079             };
11080         }
11081     };
11082     for(var fnName in Roo.Element.prototype){
11083         if(typeof Roo.Element.prototype[fnName] == "function"){
11084             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11085         }
11086     };
11087 })();
11088 /*
11089  * Based on:
11090  * Ext JS Library 1.1.1
11091  * Copyright(c) 2006-2007, Ext JS, LLC.
11092  *
11093  * Originally Released Under LGPL - original licence link has changed is not relivant.
11094  *
11095  * Fork - LGPL
11096  * <script type="text/javascript">
11097  */
11098
11099 /**
11100  * @class Roo.CompositeElementLite
11101  * @extends Roo.CompositeElement
11102  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11103  <pre><code>
11104  var els = Roo.select("#some-el div.some-class");
11105  // or select directly from an existing element
11106  var el = Roo.get('some-el');
11107  el.select('div.some-class');
11108
11109  els.setWidth(100); // all elements become 100 width
11110  els.hide(true); // all elements fade out and hide
11111  // or
11112  els.setWidth(100).hide(true);
11113  </code></pre><br><br>
11114  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11115  * actions will be performed on all the elements in this collection.</b>
11116  */
11117 Roo.CompositeElementLite = function(els){
11118     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11119     this.el = new Roo.Element.Flyweight();
11120 };
11121 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11122     addElements : function(els){
11123         if(els){
11124             if(els instanceof Array){
11125                 this.elements = this.elements.concat(els);
11126             }else{
11127                 var yels = this.elements;
11128                 var index = yels.length-1;
11129                 for(var i = 0, len = els.length; i < len; i++) {
11130                     yels[++index] = els[i];
11131                 }
11132             }
11133         }
11134         return this;
11135     },
11136     invoke : function(fn, args){
11137         var els = this.elements;
11138         var el = this.el;
11139         for(var i = 0, len = els.length; i < len; i++) {
11140             el.dom = els[i];
11141                 Roo.Element.prototype[fn].apply(el, args);
11142         }
11143         return this;
11144     },
11145     /**
11146      * Returns a flyweight Element of the dom element object at the specified index
11147      * @param {Number} index
11148      * @return {Roo.Element}
11149      */
11150     item : function(index){
11151         if(!this.elements[index]){
11152             return null;
11153         }
11154         this.el.dom = this.elements[index];
11155         return this.el;
11156     },
11157
11158     // fixes scope with flyweight
11159     addListener : function(eventName, handler, scope, opt){
11160         var els = this.elements;
11161         for(var i = 0, len = els.length; i < len; i++) {
11162             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11163         }
11164         return this;
11165     },
11166
11167     /**
11168     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11169     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11170     * a reference to the dom node, use el.dom.</b>
11171     * @param {Function} fn The function to call
11172     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11173     * @return {CompositeElement} this
11174     */
11175     each : function(fn, scope){
11176         var els = this.elements;
11177         var el = this.el;
11178         for(var i = 0, len = els.length; i < len; i++){
11179             el.dom = els[i];
11180                 if(fn.call(scope || el, el, this, i) === false){
11181                 break;
11182             }
11183         }
11184         return this;
11185     },
11186
11187     indexOf : function(el){
11188         return this.elements.indexOf(Roo.getDom(el));
11189     },
11190
11191     replaceElement : function(el, replacement, domReplace){
11192         var index = typeof el == 'number' ? el : this.indexOf(el);
11193         if(index !== -1){
11194             replacement = Roo.getDom(replacement);
11195             if(domReplace){
11196                 var d = this.elements[index];
11197                 d.parentNode.insertBefore(replacement, d);
11198                 d.parentNode.removeChild(d);
11199             }
11200             this.elements.splice(index, 1, replacement);
11201         }
11202         return this;
11203     }
11204 });
11205 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11206
11207 /*
11208  * Based on:
11209  * Ext JS Library 1.1.1
11210  * Copyright(c) 2006-2007, Ext JS, LLC.
11211  *
11212  * Originally Released Under LGPL - original licence link has changed is not relivant.
11213  *
11214  * Fork - LGPL
11215  * <script type="text/javascript">
11216  */
11217
11218  
11219
11220 /**
11221  * @class Roo.data.Connection
11222  * @extends Roo.util.Observable
11223  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11224  * either to a configured URL, or to a URL specified at request time.<br><br>
11225  * <p>
11226  * Requests made by this class are asynchronous, and will return immediately. No data from
11227  * the server will be available to the statement immediately following the {@link #request} call.
11228  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11229  * <p>
11230  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11231  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11232  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11233  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11234  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11235  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11236  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11237  * standard DOM methods.
11238  * @constructor
11239  * @param {Object} config a configuration object.
11240  */
11241 Roo.data.Connection = function(config){
11242     Roo.apply(this, config);
11243     this.addEvents({
11244         /**
11245          * @event beforerequest
11246          * Fires before a network request is made to retrieve a data object.
11247          * @param {Connection} conn This Connection object.
11248          * @param {Object} options The options config object passed to the {@link #request} method.
11249          */
11250         "beforerequest" : true,
11251         /**
11252          * @event requestcomplete
11253          * Fires if the request was successfully completed.
11254          * @param {Connection} conn This Connection object.
11255          * @param {Object} response The XHR object containing the response data.
11256          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11257          * @param {Object} options The options config object passed to the {@link #request} method.
11258          */
11259         "requestcomplete" : true,
11260         /**
11261          * @event requestexception
11262          * Fires if an error HTTP status was returned from the server.
11263          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11264          * @param {Connection} conn This Connection object.
11265          * @param {Object} response The XHR object containing the response data.
11266          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11267          * @param {Object} options The options config object passed to the {@link #request} method.
11268          */
11269         "requestexception" : true
11270     });
11271     Roo.data.Connection.superclass.constructor.call(this);
11272 };
11273
11274 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11275     /**
11276      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11277      */
11278     /**
11279      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11280      * extra parameters to each request made by this object. (defaults to undefined)
11281      */
11282     /**
11283      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11284      *  to each request made by this object. (defaults to undefined)
11285      */
11286     /**
11287      * @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)
11288      */
11289     /**
11290      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11291      */
11292     timeout : 30000,
11293     /**
11294      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11295      * @type Boolean
11296      */
11297     autoAbort:false,
11298
11299     /**
11300      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11301      * @type Boolean
11302      */
11303     disableCaching: true,
11304
11305     /**
11306      * Sends an HTTP request to a remote server.
11307      * @param {Object} options An object which may contain the following properties:<ul>
11308      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11309      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11310      * request, a url encoded string or a function to call to get either.</li>
11311      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11312      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11313      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11314      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11315      * <li>options {Object} The parameter to the request call.</li>
11316      * <li>success {Boolean} True if the request succeeded.</li>
11317      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11318      * </ul></li>
11319      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11320      * The callback is passed the following parameters:<ul>
11321      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11322      * <li>options {Object} The parameter to the request call.</li>
11323      * </ul></li>
11324      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11325      * The callback is passed the following parameters:<ul>
11326      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11327      * <li>options {Object} The parameter to the request call.</li>
11328      * </ul></li>
11329      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11330      * for the callback function. Defaults to the browser window.</li>
11331      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11332      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11333      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11334      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11335      * params for the post data. Any params will be appended to the URL.</li>
11336      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11337      * </ul>
11338      * @return {Number} transactionId
11339      */
11340     request : function(o){
11341         if(this.fireEvent("beforerequest", this, o) !== false){
11342             var p = o.params;
11343
11344             if(typeof p == "function"){
11345                 p = p.call(o.scope||window, o);
11346             }
11347             if(typeof p == "object"){
11348                 p = Roo.urlEncode(o.params);
11349             }
11350             if(this.extraParams){
11351                 var extras = Roo.urlEncode(this.extraParams);
11352                 p = p ? (p + '&' + extras) : extras;
11353             }
11354
11355             var url = o.url || this.url;
11356             if(typeof url == 'function'){
11357                 url = url.call(o.scope||window, o);
11358             }
11359
11360             if(o.form){
11361                 var form = Roo.getDom(o.form);
11362                 url = url || form.action;
11363
11364                 var enctype = form.getAttribute("enctype");
11365                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11366                     return this.doFormUpload(o, p, url);
11367                 }
11368                 var f = Roo.lib.Ajax.serializeForm(form);
11369                 p = p ? (p + '&' + f) : f;
11370             }
11371
11372             var hs = o.headers;
11373             if(this.defaultHeaders){
11374                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11375                 if(!o.headers){
11376                     o.headers = hs;
11377                 }
11378             }
11379
11380             var cb = {
11381                 success: this.handleResponse,
11382                 failure: this.handleFailure,
11383                 scope: this,
11384                 argument: {options: o},
11385                 timeout : this.timeout
11386             };
11387
11388             var method = o.method||this.method||(p ? "POST" : "GET");
11389
11390             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11391                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11392             }
11393
11394             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11395                 if(o.autoAbort){
11396                     this.abort();
11397                 }
11398             }else if(this.autoAbort !== false){
11399                 this.abort();
11400             }
11401
11402             if((method == 'GET' && p) || o.xmlData){
11403                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11404                 p = '';
11405             }
11406             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11407             return this.transId;
11408         }else{
11409             Roo.callback(o.callback, o.scope, [o, null, null]);
11410             return null;
11411         }
11412     },
11413
11414     /**
11415      * Determine whether this object has a request outstanding.
11416      * @param {Number} transactionId (Optional) defaults to the last transaction
11417      * @return {Boolean} True if there is an outstanding request.
11418      */
11419     isLoading : function(transId){
11420         if(transId){
11421             return Roo.lib.Ajax.isCallInProgress(transId);
11422         }else{
11423             return this.transId ? true : false;
11424         }
11425     },
11426
11427     /**
11428      * Aborts any outstanding request.
11429      * @param {Number} transactionId (Optional) defaults to the last transaction
11430      */
11431     abort : function(transId){
11432         if(transId || this.isLoading()){
11433             Roo.lib.Ajax.abort(transId || this.transId);
11434         }
11435     },
11436
11437     // private
11438     handleResponse : function(response){
11439         this.transId = false;
11440         var options = response.argument.options;
11441         response.argument = options ? options.argument : null;
11442         this.fireEvent("requestcomplete", this, response, options);
11443         Roo.callback(options.success, options.scope, [response, options]);
11444         Roo.callback(options.callback, options.scope, [options, true, response]);
11445     },
11446
11447     // private
11448     handleFailure : function(response, e){
11449         this.transId = false;
11450         var options = response.argument.options;
11451         response.argument = options ? options.argument : null;
11452         this.fireEvent("requestexception", this, response, options, e);
11453         Roo.callback(options.failure, options.scope, [response, options]);
11454         Roo.callback(options.callback, options.scope, [options, false, response]);
11455     },
11456
11457     // private
11458     doFormUpload : function(o, ps, url){
11459         var id = Roo.id();
11460         var frame = document.createElement('iframe');
11461         frame.id = id;
11462         frame.name = id;
11463         frame.className = 'x-hidden';
11464         if(Roo.isIE){
11465             frame.src = Roo.SSL_SECURE_URL;
11466         }
11467         document.body.appendChild(frame);
11468
11469         if(Roo.isIE){
11470            document.frames[id].name = id;
11471         }
11472
11473         var form = Roo.getDom(o.form);
11474         form.target = id;
11475         form.method = 'POST';
11476         form.enctype = form.encoding = 'multipart/form-data';
11477         if(url){
11478             form.action = url;
11479         }
11480
11481         var hiddens, hd;
11482         if(ps){ // add dynamic params
11483             hiddens = [];
11484             ps = Roo.urlDecode(ps, false);
11485             for(var k in ps){
11486                 if(ps.hasOwnProperty(k)){
11487                     hd = document.createElement('input');
11488                     hd.type = 'hidden';
11489                     hd.name = k;
11490                     hd.value = ps[k];
11491                     form.appendChild(hd);
11492                     hiddens.push(hd);
11493                 }
11494             }
11495         }
11496
11497         function cb(){
11498             var r = {  // bogus response object
11499                 responseText : '',
11500                 responseXML : null
11501             };
11502
11503             r.argument = o ? o.argument : null;
11504
11505             try { //
11506                 var doc;
11507                 if(Roo.isIE){
11508                     doc = frame.contentWindow.document;
11509                 }else {
11510                     doc = (frame.contentDocument || window.frames[id].document);
11511                 }
11512                 if(doc && doc.body){
11513                     r.responseText = doc.body.innerHTML;
11514                 }
11515                 if(doc && doc.XMLDocument){
11516                     r.responseXML = doc.XMLDocument;
11517                 }else {
11518                     r.responseXML = doc;
11519                 }
11520             }
11521             catch(e) {
11522                 // ignore
11523             }
11524
11525             Roo.EventManager.removeListener(frame, 'load', cb, this);
11526
11527             this.fireEvent("requestcomplete", this, r, o);
11528             Roo.callback(o.success, o.scope, [r, o]);
11529             Roo.callback(o.callback, o.scope, [o, true, r]);
11530
11531             setTimeout(function(){document.body.removeChild(frame);}, 100);
11532         }
11533
11534         Roo.EventManager.on(frame, 'load', cb, this);
11535         form.submit();
11536
11537         if(hiddens){ // remove dynamic params
11538             for(var i = 0, len = hiddens.length; i < len; i++){
11539                 form.removeChild(hiddens[i]);
11540             }
11541         }
11542     }
11543 });
11544
11545 /**
11546  * @class Roo.Ajax
11547  * @extends Roo.data.Connection
11548  * Global Ajax request class.
11549  *
11550  * @singleton
11551  */
11552 Roo.Ajax = new Roo.data.Connection({
11553     // fix up the docs
11554    /**
11555      * @cfg {String} url @hide
11556      */
11557     /**
11558      * @cfg {Object} extraParams @hide
11559      */
11560     /**
11561      * @cfg {Object} defaultHeaders @hide
11562      */
11563     /**
11564      * @cfg {String} method (Optional) @hide
11565      */
11566     /**
11567      * @cfg {Number} timeout (Optional) @hide
11568      */
11569     /**
11570      * @cfg {Boolean} autoAbort (Optional) @hide
11571      */
11572
11573     /**
11574      * @cfg {Boolean} disableCaching (Optional) @hide
11575      */
11576
11577     /**
11578      * @property  disableCaching
11579      * True to add a unique cache-buster param to GET requests. (defaults to true)
11580      * @type Boolean
11581      */
11582     /**
11583      * @property  url
11584      * The default URL to be used for requests to the server. (defaults to undefined)
11585      * @type String
11586      */
11587     /**
11588      * @property  extraParams
11589      * An object containing properties which are used as
11590      * extra parameters to each request made by this object. (defaults to undefined)
11591      * @type Object
11592      */
11593     /**
11594      * @property  defaultHeaders
11595      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11596      * @type Object
11597      */
11598     /**
11599      * @property  method
11600      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11601      * @type String
11602      */
11603     /**
11604      * @property  timeout
11605      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11606      * @type Number
11607      */
11608
11609     /**
11610      * @property  autoAbort
11611      * Whether a new request should abort any pending requests. (defaults to false)
11612      * @type Boolean
11613      */
11614     autoAbort : false,
11615
11616     /**
11617      * Serialize the passed form into a url encoded string
11618      * @param {String/HTMLElement} form
11619      * @return {String}
11620      */
11621     serializeForm : function(form){
11622         return Roo.lib.Ajax.serializeForm(form);
11623     }
11624 });/*
11625  * Based on:
11626  * Ext JS Library 1.1.1
11627  * Copyright(c) 2006-2007, Ext JS, LLC.
11628  *
11629  * Originally Released Under LGPL - original licence link has changed is not relivant.
11630  *
11631  * Fork - LGPL
11632  * <script type="text/javascript">
11633  */
11634  
11635 /**
11636  * Global Ajax request class.
11637  * 
11638  * @class Roo.Ajax
11639  * @extends Roo.data.Connection
11640  * @static
11641  * 
11642  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11643  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11644  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11645  * @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)
11646  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11647  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11648  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11649  */
11650 Roo.Ajax = new Roo.data.Connection({
11651     // fix up the docs
11652     /**
11653      * @scope Roo.Ajax
11654      * @type {Boolear} 
11655      */
11656     autoAbort : false,
11657
11658     /**
11659      * Serialize the passed form into a url encoded string
11660      * @scope Roo.Ajax
11661      * @param {String/HTMLElement} form
11662      * @return {String}
11663      */
11664     serializeForm : function(form){
11665         return Roo.lib.Ajax.serializeForm(form);
11666     }
11667 });/*
11668  * Based on:
11669  * Ext JS Library 1.1.1
11670  * Copyright(c) 2006-2007, Ext JS, LLC.
11671  *
11672  * Originally Released Under LGPL - original licence link has changed is not relivant.
11673  *
11674  * Fork - LGPL
11675  * <script type="text/javascript">
11676  */
11677
11678  
11679 /**
11680  * @class Roo.UpdateManager
11681  * @extends Roo.util.Observable
11682  * Provides AJAX-style update for Element object.<br><br>
11683  * Usage:<br>
11684  * <pre><code>
11685  * // Get it from a Roo.Element object
11686  * var el = Roo.get("foo");
11687  * var mgr = el.getUpdateManager();
11688  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11689  * ...
11690  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11691  * <br>
11692  * // or directly (returns the same UpdateManager instance)
11693  * var mgr = new Roo.UpdateManager("myElementId");
11694  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11695  * mgr.on("update", myFcnNeedsToKnow);
11696  * <br>
11697    // short handed call directly from the element object
11698    Roo.get("foo").load({
11699         url: "bar.php",
11700         scripts:true,
11701         params: "for=bar",
11702         text: "Loading Foo..."
11703    });
11704  * </code></pre>
11705  * @constructor
11706  * Create new UpdateManager directly.
11707  * @param {String/HTMLElement/Roo.Element} el The element to update
11708  * @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).
11709  */
11710 Roo.UpdateManager = function(el, forceNew){
11711     el = Roo.get(el);
11712     if(!forceNew && el.updateManager){
11713         return el.updateManager;
11714     }
11715     /**
11716      * The Element object
11717      * @type Roo.Element
11718      */
11719     this.el = el;
11720     /**
11721      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11722      * @type String
11723      */
11724     this.defaultUrl = null;
11725
11726     this.addEvents({
11727         /**
11728          * @event beforeupdate
11729          * Fired before an update is made, return false from your handler and the update is cancelled.
11730          * @param {Roo.Element} el
11731          * @param {String/Object/Function} url
11732          * @param {String/Object} params
11733          */
11734         "beforeupdate": true,
11735         /**
11736          * @event update
11737          * Fired after successful update is made.
11738          * @param {Roo.Element} el
11739          * @param {Object} oResponseObject The response Object
11740          */
11741         "update": true,
11742         /**
11743          * @event failure
11744          * Fired on update failure.
11745          * @param {Roo.Element} el
11746          * @param {Object} oResponseObject The response Object
11747          */
11748         "failure": true
11749     });
11750     var d = Roo.UpdateManager.defaults;
11751     /**
11752      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11753      * @type String
11754      */
11755     this.sslBlankUrl = d.sslBlankUrl;
11756     /**
11757      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11758      * @type Boolean
11759      */
11760     this.disableCaching = d.disableCaching;
11761     /**
11762      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11763      * @type String
11764      */
11765     this.indicatorText = d.indicatorText;
11766     /**
11767      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11768      * @type String
11769      */
11770     this.showLoadIndicator = d.showLoadIndicator;
11771     /**
11772      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11773      * @type Number
11774      */
11775     this.timeout = d.timeout;
11776
11777     /**
11778      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11779      * @type Boolean
11780      */
11781     this.loadScripts = d.loadScripts;
11782
11783     /**
11784      * Transaction object of current executing transaction
11785      */
11786     this.transaction = null;
11787
11788     /**
11789      * @private
11790      */
11791     this.autoRefreshProcId = null;
11792     /**
11793      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11794      * @type Function
11795      */
11796     this.refreshDelegate = this.refresh.createDelegate(this);
11797     /**
11798      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11799      * @type Function
11800      */
11801     this.updateDelegate = this.update.createDelegate(this);
11802     /**
11803      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11804      * @type Function
11805      */
11806     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11807     /**
11808      * @private
11809      */
11810     this.successDelegate = this.processSuccess.createDelegate(this);
11811     /**
11812      * @private
11813      */
11814     this.failureDelegate = this.processFailure.createDelegate(this);
11815
11816     if(!this.renderer){
11817      /**
11818       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11819       */
11820     this.renderer = new Roo.UpdateManager.BasicRenderer();
11821     }
11822     
11823     Roo.UpdateManager.superclass.constructor.call(this);
11824 };
11825
11826 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11827     /**
11828      * Get the Element this UpdateManager is bound to
11829      * @return {Roo.Element} The element
11830      */
11831     getEl : function(){
11832         return this.el;
11833     },
11834     /**
11835      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11836      * @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:
11837 <pre><code>
11838 um.update({<br/>
11839     url: "your-url.php",<br/>
11840     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11841     callback: yourFunction,<br/>
11842     scope: yourObject, //(optional scope)  <br/>
11843     discardUrl: false, <br/>
11844     nocache: false,<br/>
11845     text: "Loading...",<br/>
11846     timeout: 30,<br/>
11847     scripts: false<br/>
11848 });
11849 </code></pre>
11850      * The only required property is url. The optional properties nocache, text and scripts
11851      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11852      * @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}
11853      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11854      * @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.
11855      */
11856     update : function(url, params, callback, discardUrl){
11857         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11858             var method = this.method, cfg;
11859             if(typeof url == "object"){ // must be config object
11860                 cfg = url;
11861                 url = cfg.url;
11862                 params = params || cfg.params;
11863                 callback = callback || cfg.callback;
11864                 discardUrl = discardUrl || cfg.discardUrl;
11865                 if(callback && cfg.scope){
11866                     callback = callback.createDelegate(cfg.scope);
11867                 }
11868                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11869                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11870                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11871                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11872                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11873             }
11874             this.showLoading();
11875             if(!discardUrl){
11876                 this.defaultUrl = url;
11877             }
11878             if(typeof url == "function"){
11879                 url = url.call(this);
11880             }
11881
11882             method = method || (params ? "POST" : "GET");
11883             if(method == "GET"){
11884                 url = this.prepareUrl(url);
11885             }
11886
11887             var o = Roo.apply(cfg ||{}, {
11888                 url : url,
11889                 params: params,
11890                 success: this.successDelegate,
11891                 failure: this.failureDelegate,
11892                 callback: undefined,
11893                 timeout: (this.timeout*1000),
11894                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11895             });
11896
11897             this.transaction = Roo.Ajax.request(o);
11898         }
11899     },
11900
11901     /**
11902      * 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.
11903      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11904      * @param {String/HTMLElement} form The form Id or form element
11905      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11906      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11907      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11908      */
11909     formUpdate : function(form, url, reset, callback){
11910         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11911             if(typeof url == "function"){
11912                 url = url.call(this);
11913             }
11914             form = Roo.getDom(form);
11915             this.transaction = Roo.Ajax.request({
11916                 form: form,
11917                 url:url,
11918                 success: this.successDelegate,
11919                 failure: this.failureDelegate,
11920                 timeout: (this.timeout*1000),
11921                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11922             });
11923             this.showLoading.defer(1, this);
11924         }
11925     },
11926
11927     /**
11928      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11929      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11930      */
11931     refresh : function(callback){
11932         if(this.defaultUrl == null){
11933             return;
11934         }
11935         this.update(this.defaultUrl, null, callback, true);
11936     },
11937
11938     /**
11939      * Set this element to auto refresh.
11940      * @param {Number} interval How often to update (in seconds).
11941      * @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)
11942      * @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}
11943      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11944      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11945      */
11946     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11947         if(refreshNow){
11948             this.update(url || this.defaultUrl, params, callback, true);
11949         }
11950         if(this.autoRefreshProcId){
11951             clearInterval(this.autoRefreshProcId);
11952         }
11953         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11954     },
11955
11956     /**
11957      * Stop auto refresh on this element.
11958      */
11959      stopAutoRefresh : function(){
11960         if(this.autoRefreshProcId){
11961             clearInterval(this.autoRefreshProcId);
11962             delete this.autoRefreshProcId;
11963         }
11964     },
11965
11966     isAutoRefreshing : function(){
11967        return this.autoRefreshProcId ? true : false;
11968     },
11969     /**
11970      * Called to update the element to "Loading" state. Override to perform custom action.
11971      */
11972     showLoading : function(){
11973         if(this.showLoadIndicator){
11974             this.el.update(this.indicatorText);
11975         }
11976     },
11977
11978     /**
11979      * Adds unique parameter to query string if disableCaching = true
11980      * @private
11981      */
11982     prepareUrl : function(url){
11983         if(this.disableCaching){
11984             var append = "_dc=" + (new Date().getTime());
11985             if(url.indexOf("?") !== -1){
11986                 url += "&" + append;
11987             }else{
11988                 url += "?" + append;
11989             }
11990         }
11991         return url;
11992     },
11993
11994     /**
11995      * @private
11996      */
11997     processSuccess : function(response){
11998         this.transaction = null;
11999         if(response.argument.form && response.argument.reset){
12000             try{ // put in try/catch since some older FF releases had problems with this
12001                 response.argument.form.reset();
12002             }catch(e){}
12003         }
12004         if(this.loadScripts){
12005             this.renderer.render(this.el, response, this,
12006                 this.updateComplete.createDelegate(this, [response]));
12007         }else{
12008             this.renderer.render(this.el, response, this);
12009             this.updateComplete(response);
12010         }
12011     },
12012
12013     updateComplete : function(response){
12014         this.fireEvent("update", this.el, response);
12015         if(typeof response.argument.callback == "function"){
12016             response.argument.callback(this.el, true, response);
12017         }
12018     },
12019
12020     /**
12021      * @private
12022      */
12023     processFailure : function(response){
12024         this.transaction = null;
12025         this.fireEvent("failure", this.el, response);
12026         if(typeof response.argument.callback == "function"){
12027             response.argument.callback(this.el, false, response);
12028         }
12029     },
12030
12031     /**
12032      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12033      * @param {Object} renderer The object implementing the render() method
12034      */
12035     setRenderer : function(renderer){
12036         this.renderer = renderer;
12037     },
12038
12039     getRenderer : function(){
12040        return this.renderer;
12041     },
12042
12043     /**
12044      * Set the defaultUrl used for updates
12045      * @param {String/Function} defaultUrl The url or a function to call to get the url
12046      */
12047     setDefaultUrl : function(defaultUrl){
12048         this.defaultUrl = defaultUrl;
12049     },
12050
12051     /**
12052      * Aborts the executing transaction
12053      */
12054     abort : function(){
12055         if(this.transaction){
12056             Roo.Ajax.abort(this.transaction);
12057         }
12058     },
12059
12060     /**
12061      * Returns true if an update is in progress
12062      * @return {Boolean}
12063      */
12064     isUpdating : function(){
12065         if(this.transaction){
12066             return Roo.Ajax.isLoading(this.transaction);
12067         }
12068         return false;
12069     }
12070 });
12071
12072 /**
12073  * @class Roo.UpdateManager.defaults
12074  * @static (not really - but it helps the doc tool)
12075  * The defaults collection enables customizing the default properties of UpdateManager
12076  */
12077    Roo.UpdateManager.defaults = {
12078        /**
12079          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12080          * @type Number
12081          */
12082          timeout : 30,
12083
12084          /**
12085          * True to process scripts by default (Defaults to false).
12086          * @type Boolean
12087          */
12088         loadScripts : false,
12089
12090         /**
12091         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12092         * @type String
12093         */
12094         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12095         /**
12096          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12097          * @type Boolean
12098          */
12099         disableCaching : false,
12100         /**
12101          * Whether to show indicatorText when loading (Defaults to true).
12102          * @type Boolean
12103          */
12104         showLoadIndicator : true,
12105         /**
12106          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12107          * @type String
12108          */
12109         indicatorText : '<div class="loading-indicator">Loading...</div>'
12110    };
12111
12112 /**
12113  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12114  *Usage:
12115  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12116  * @param {String/HTMLElement/Roo.Element} el The element to update
12117  * @param {String} url The url
12118  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12119  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12120  * @static
12121  * @deprecated
12122  * @member Roo.UpdateManager
12123  */
12124 Roo.UpdateManager.updateElement = function(el, url, params, options){
12125     var um = Roo.get(el, true).getUpdateManager();
12126     Roo.apply(um, options);
12127     um.update(url, params, options ? options.callback : null);
12128 };
12129 // alias for backwards compat
12130 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12131 /**
12132  * @class Roo.UpdateManager.BasicRenderer
12133  * Default Content renderer. Updates the elements innerHTML with the responseText.
12134  */
12135 Roo.UpdateManager.BasicRenderer = function(){};
12136
12137 Roo.UpdateManager.BasicRenderer.prototype = {
12138     /**
12139      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12140      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12141      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12142      * @param {Roo.Element} el The element being rendered
12143      * @param {Object} response The YUI Connect response object
12144      * @param {UpdateManager} updateManager The calling update manager
12145      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12146      */
12147      render : function(el, response, updateManager, callback){
12148         el.update(response.responseText, updateManager.loadScripts, callback);
12149     }
12150 };
12151 /*
12152  * Based on:
12153  * Roo JS
12154  * (c)) Alan Knowles
12155  * Licence : LGPL
12156  */
12157
12158
12159 /**
12160  * @class Roo.DomTemplate
12161  * @extends Roo.Template
12162  * An effort at a dom based template engine..
12163  *
12164  * Similar to XTemplate, except it uses dom parsing to create the template..
12165  *
12166  * Supported features:
12167  *
12168  *  Tags:
12169
12170 <pre><code>
12171       {a_variable} - output encoded.
12172       {a_variable.format:("Y-m-d")} - call a method on the variable
12173       {a_variable:raw} - unencoded output
12174       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12175       {a_variable:this.method_on_template(...)} - call a method on the template object.
12176  
12177 </code></pre>
12178  *  The tpl tag:
12179 <pre><code>
12180         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12181         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12182         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12183         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12184   
12185 </code></pre>
12186  *      
12187  */
12188 Roo.DomTemplate = function()
12189 {
12190      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12191     if (this.html) {
12192         this.compile();
12193     }
12194 };
12195
12196
12197 Roo.extend(Roo.DomTemplate, Roo.Template, {
12198     /**
12199      * id counter for sub templates.
12200      */
12201     id : 0,
12202     /**
12203      * flag to indicate if dom parser is inside a pre,
12204      * it will strip whitespace if not.
12205      */
12206     inPre : false,
12207     
12208     /**
12209      * The various sub templates
12210      */
12211     tpls : false,
12212     
12213     
12214     
12215     /**
12216      *
12217      * basic tag replacing syntax
12218      * WORD:WORD()
12219      *
12220      * // you can fake an object call by doing this
12221      *  x.t:(test,tesT) 
12222      * 
12223      */
12224     re : /(\{|%7B])([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\}|%7D)/g,
12225     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12226     
12227     iterChild : function (node, method) {
12228         
12229         var oldPre = this.inPre;
12230         if (node.tagName == 'PRE') {
12231             this.inPre = true;
12232         }
12233         for( var i = 0; i < node.childNodes.length; i++) {
12234             method.call(this, node.childNodes[i]);
12235         }
12236         this.inPre = oldPre;
12237     },
12238     
12239     
12240     
12241     /**
12242      * compile the template
12243      *
12244      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12245      *
12246      */
12247     compile: function()
12248     {
12249         var s = this.html;
12250         
12251         // covert the html into DOM...
12252         
12253         var div = document.createElement('div');
12254         div.innerHTML =   this.html  ;
12255         
12256         this.tpls = [];
12257         var _t = this;
12258         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12259         
12260         var tpls = this.tpls;
12261         
12262         // create a top level template from the snippet..
12263         
12264         Roo.log(div.innerHTML);
12265         
12266         var tpl = {
12267             uid : 'master',
12268             id : this.id++,
12269             attr : false,
12270             value : false,
12271             body : div.innerHTML,
12272             
12273             forCall : false,
12274             execCall : false,
12275             dom : div,
12276             isTop : true
12277             
12278         };
12279         tpls.unshift(tpl);
12280         
12281         
12282         // compile them...
12283         this.tpls = [];
12284         Roo.each(tpls, function(tp){
12285             this.compileTpl(tp);
12286             this.tpls[tp.id] = tp;
12287         }, this);
12288         
12289         this.master = tpls[0];
12290         return this;
12291         
12292         
12293     },
12294     
12295     compileNode : function(node, istop) {
12296         // test for
12297         //Roo.log(node);
12298         
12299         
12300         // skip anything not a tag..
12301         if (node.nodeType != 1) {
12302             if (node.nodeType == 3 && !this.inPre) {
12303                 // reduce white space..
12304                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12305                 
12306             }
12307             return;
12308         }
12309         
12310         var tpl = {
12311             uid : false,
12312             id : false,
12313             attr : false,
12314             value : false,
12315             body : '',
12316             
12317             forCall : false,
12318             execCall : false,
12319             dom : false,
12320             isTop : istop
12321             
12322             
12323         };
12324         
12325         
12326         switch(true) {
12327             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12328             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12329             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12330             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12331             // no default..
12332         }
12333         
12334         
12335         if (!tpl.attr) {
12336             // just itterate children..
12337             this.iterChild(node,this.compileNode);
12338             return;
12339         }
12340         tpl.uid = this.id++;
12341         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12342         node.removeAttribute('roo-'+ tpl.attr);
12343         if (tpl.attr != 'name') {
12344             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12345             node.parentNode.replaceChild(placeholder,  node);
12346         } else {
12347             
12348             var placeholder =  document.createElement('span');
12349             placeholder.className = 'roo-tpl-' + tpl.value;
12350             node.parentNode.replaceChild(placeholder,  node);
12351         }
12352         
12353         // parent now sees '{domtplXXXX}
12354         this.iterChild(node,this.compileNode);
12355         
12356         // we should now have node body...
12357         var div = document.createElement('div');
12358         div.appendChild(node);
12359         tpl.dom = node;
12360         // this has the unfortunate side effect of converting tagged attributes
12361         // eg. href="{...}" into %7C...%7D
12362         // this has been fixed by searching for those combo's although it's a bit hacky..
12363         
12364         
12365         tpl.body = div.innerHTML;
12366         
12367         
12368          
12369         tpl.id = tpl.uid;
12370         switch(tpl.attr) {
12371             case 'for' :
12372                 switch (tpl.value) {
12373                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12374                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12375                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12376                 }
12377                 break;
12378             
12379             case 'exec':
12380                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12381                 break;
12382             
12383             case 'if':     
12384                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12385                 break;
12386             
12387             case 'name':
12388                 tpl.id  = tpl.value; // replace non characters???
12389                 break;
12390             
12391         }
12392         
12393         
12394         this.tpls.push(tpl);
12395         
12396         
12397         
12398     },
12399     
12400     
12401     
12402     
12403     /**
12404      * Compile a segment of the template into a 'sub-template'
12405      *
12406      * 
12407      * 
12408      *
12409      */
12410     compileTpl : function(tpl)
12411     {
12412         var fm = Roo.util.Format;
12413         var useF = this.disableFormats !== true;
12414         
12415         var sep = Roo.isGecko ? "+\n" : ",\n";
12416         
12417         var undef = function(str) {
12418             Roo.log("Property not found :"  + str);
12419             return '';
12420         };
12421           
12422         
12423         var fn = function(m, lbrace, name, format, args)
12424         {
12425             //Roo.log("ARGS");
12426             //Roo.log(arguments);
12427             args = args ? args.replace(/\\'/g,"'") : args;
12428             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12429             if (typeof(format) == 'undefined') {
12430                 format =  'htmlEncode'; 
12431             }
12432             if (format == 'raw' ) {
12433                 format = false;
12434             }
12435             
12436             if(name.substr(0, 6) == 'domtpl'){
12437                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12438             }
12439             
12440             // build an array of options to determine if value is undefined..
12441             
12442             // basically get 'xxxx.yyyy' then do
12443             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12444             //    (function () { Roo.log("Property not found"); return ''; })() :
12445             //    ......
12446             
12447             var udef_ar = [];
12448             var lookfor = '';
12449             Roo.each(name.split('.'), function(st) {
12450                 lookfor += (lookfor.length ? '.': '') + st;
12451                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12452             });
12453             
12454             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12455             
12456             
12457             if(format && useF){
12458                 
12459                 args = args ? ',' + args : "";
12460                  
12461                 if(format.substr(0, 5) != "this."){
12462                     format = "fm." + format + '(';
12463                 }else{
12464                     format = 'this.call("'+ format.substr(5) + '", ';
12465                     args = ", values";
12466                 }
12467                 
12468                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12469             }
12470              
12471             if (args.length) {
12472                 // called with xxyx.yuu:(test,test)
12473                 // change to ()
12474                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12475             }
12476             // raw.. - :raw modifier..
12477             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12478             
12479         };
12480         var body;
12481         // branched to use + in gecko and [].join() in others
12482         if(Roo.isGecko){
12483             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12484                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12485                     "';};};";
12486         }else{
12487             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12488             body.push(tpl.body.replace(/(\r\n|\n)/g,
12489                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12490             body.push("'].join('');};};");
12491             body = body.join('');
12492         }
12493         
12494         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12495        
12496         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12497         eval(body);
12498         
12499         return this;
12500     },
12501      
12502     /**
12503      * same as applyTemplate, except it's done to one of the subTemplates
12504      * when using named templates, you can do:
12505      *
12506      * var str = pl.applySubTemplate('your-name', values);
12507      *
12508      * 
12509      * @param {Number} id of the template
12510      * @param {Object} values to apply to template
12511      * @param {Object} parent (normaly the instance of this object)
12512      */
12513     applySubTemplate : function(id, values, parent)
12514     {
12515         
12516         
12517         var t = this.tpls[id];
12518         
12519         
12520         try { 
12521             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12522                 Roo.log('if call on ' + t.value + ' return false');
12523                 return '';
12524             }
12525         } catch(e) {
12526             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12527             Roo.log(values);
12528           
12529             return '';
12530         }
12531         try { 
12532             
12533             if(t.execCall && t.execCall.call(this, values, parent)){
12534                 return '';
12535             }
12536         } catch(e) {
12537             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12538             Roo.log(values);
12539             return '';
12540         }
12541         
12542         try {
12543             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12544             parent = t.target ? values : parent;
12545             if(t.forCall && vs instanceof Array){
12546                 var buf = [];
12547                 for(var i = 0, len = vs.length; i < len; i++){
12548                     try {
12549                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12550                     } catch (e) {
12551                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12552                         Roo.log(e.body);
12553                         //Roo.log(t.compiled);
12554                         Roo.log(vs[i]);
12555                     }   
12556                 }
12557                 return buf.join('');
12558             }
12559         } catch (e) {
12560             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12561             Roo.log(values);
12562             return '';
12563         }
12564         try {
12565             return t.compiled.call(this, vs, parent);
12566         } catch (e) {
12567             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12568             Roo.log(e.body);
12569             //Roo.log(t.compiled);
12570             Roo.log(values);
12571             return '';
12572         }
12573     },
12574
12575    
12576
12577     applyTemplate : function(values){
12578         return this.master.compiled.call(this, values, {});
12579         //var s = this.subs;
12580     },
12581
12582     apply : function(){
12583         return this.applyTemplate.apply(this, arguments);
12584     }
12585
12586  });
12587
12588 Roo.DomTemplate.from = function(el){
12589     el = Roo.getDom(el);
12590     return new Roo.Domtemplate(el.value || el.innerHTML);
12591 };/*
12592  * Based on:
12593  * Ext JS Library 1.1.1
12594  * Copyright(c) 2006-2007, Ext JS, LLC.
12595  *
12596  * Originally Released Under LGPL - original licence link has changed is not relivant.
12597  *
12598  * Fork - LGPL
12599  * <script type="text/javascript">
12600  */
12601
12602 /**
12603  * @class Roo.util.DelayedTask
12604  * Provides a convenient method of performing setTimeout where a new
12605  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12606  * You can use this class to buffer
12607  * the keypress events for a certain number of milliseconds, and perform only if they stop
12608  * for that amount of time.
12609  * @constructor The parameters to this constructor serve as defaults and are not required.
12610  * @param {Function} fn (optional) The default function to timeout
12611  * @param {Object} scope (optional) The default scope of that timeout
12612  * @param {Array} args (optional) The default Array of arguments
12613  */
12614 Roo.util.DelayedTask = function(fn, scope, args){
12615     var id = null, d, t;
12616
12617     var call = function(){
12618         var now = new Date().getTime();
12619         if(now - t >= d){
12620             clearInterval(id);
12621             id = null;
12622             fn.apply(scope, args || []);
12623         }
12624     };
12625     /**
12626      * Cancels any pending timeout and queues a new one
12627      * @param {Number} delay The milliseconds to delay
12628      * @param {Function} newFn (optional) Overrides function passed to constructor
12629      * @param {Object} newScope (optional) Overrides scope passed to constructor
12630      * @param {Array} newArgs (optional) Overrides args passed to constructor
12631      */
12632     this.delay = function(delay, newFn, newScope, newArgs){
12633         if(id && delay != d){
12634             this.cancel();
12635         }
12636         d = delay;
12637         t = new Date().getTime();
12638         fn = newFn || fn;
12639         scope = newScope || scope;
12640         args = newArgs || args;
12641         if(!id){
12642             id = setInterval(call, d);
12643         }
12644     };
12645
12646     /**
12647      * Cancel the last queued timeout
12648      */
12649     this.cancel = function(){
12650         if(id){
12651             clearInterval(id);
12652             id = null;
12653         }
12654     };
12655 };/*
12656  * Based on:
12657  * Ext JS Library 1.1.1
12658  * Copyright(c) 2006-2007, Ext JS, LLC.
12659  *
12660  * Originally Released Under LGPL - original licence link has changed is not relivant.
12661  *
12662  * Fork - LGPL
12663  * <script type="text/javascript">
12664  */
12665  
12666  
12667 Roo.util.TaskRunner = function(interval){
12668     interval = interval || 10;
12669     var tasks = [], removeQueue = [];
12670     var id = 0;
12671     var running = false;
12672
12673     var stopThread = function(){
12674         running = false;
12675         clearInterval(id);
12676         id = 0;
12677     };
12678
12679     var startThread = function(){
12680         if(!running){
12681             running = true;
12682             id = setInterval(runTasks, interval);
12683         }
12684     };
12685
12686     var removeTask = function(task){
12687         removeQueue.push(task);
12688         if(task.onStop){
12689             task.onStop();
12690         }
12691     };
12692
12693     var runTasks = function(){
12694         if(removeQueue.length > 0){
12695             for(var i = 0, len = removeQueue.length; i < len; i++){
12696                 tasks.remove(removeQueue[i]);
12697             }
12698             removeQueue = [];
12699             if(tasks.length < 1){
12700                 stopThread();
12701                 return;
12702             }
12703         }
12704         var now = new Date().getTime();
12705         for(var i = 0, len = tasks.length; i < len; ++i){
12706             var t = tasks[i];
12707             var itime = now - t.taskRunTime;
12708             if(t.interval <= itime){
12709                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12710                 t.taskRunTime = now;
12711                 if(rt === false || t.taskRunCount === t.repeat){
12712                     removeTask(t);
12713                     return;
12714                 }
12715             }
12716             if(t.duration && t.duration <= (now - t.taskStartTime)){
12717                 removeTask(t);
12718             }
12719         }
12720     };
12721
12722     /**
12723      * Queues a new task.
12724      * @param {Object} task
12725      */
12726     this.start = function(task){
12727         tasks.push(task);
12728         task.taskStartTime = new Date().getTime();
12729         task.taskRunTime = 0;
12730         task.taskRunCount = 0;
12731         startThread();
12732         return task;
12733     };
12734
12735     this.stop = function(task){
12736         removeTask(task);
12737         return task;
12738     };
12739
12740     this.stopAll = function(){
12741         stopThread();
12742         for(var i = 0, len = tasks.length; i < len; i++){
12743             if(tasks[i].onStop){
12744                 tasks[i].onStop();
12745             }
12746         }
12747         tasks = [];
12748         removeQueue = [];
12749     };
12750 };
12751
12752 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12753  * Based on:
12754  * Ext JS Library 1.1.1
12755  * Copyright(c) 2006-2007, Ext JS, LLC.
12756  *
12757  * Originally Released Under LGPL - original licence link has changed is not relivant.
12758  *
12759  * Fork - LGPL
12760  * <script type="text/javascript">
12761  */
12762
12763  
12764 /**
12765  * @class Roo.util.MixedCollection
12766  * @extends Roo.util.Observable
12767  * A Collection class that maintains both numeric indexes and keys and exposes events.
12768  * @constructor
12769  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12770  * collection (defaults to false)
12771  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12772  * and return the key value for that item.  This is used when available to look up the key on items that
12773  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12774  * equivalent to providing an implementation for the {@link #getKey} method.
12775  */
12776 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12777     this.items = [];
12778     this.map = {};
12779     this.keys = [];
12780     this.length = 0;
12781     this.addEvents({
12782         /**
12783          * @event clear
12784          * Fires when the collection is cleared.
12785          */
12786         "clear" : true,
12787         /**
12788          * @event add
12789          * Fires when an item is added to the collection.
12790          * @param {Number} index The index at which the item was added.
12791          * @param {Object} o The item added.
12792          * @param {String} key The key associated with the added item.
12793          */
12794         "add" : true,
12795         /**
12796          * @event replace
12797          * Fires when an item is replaced in the collection.
12798          * @param {String} key he key associated with the new added.
12799          * @param {Object} old The item being replaced.
12800          * @param {Object} new The new item.
12801          */
12802         "replace" : true,
12803         /**
12804          * @event remove
12805          * Fires when an item is removed from the collection.
12806          * @param {Object} o The item being removed.
12807          * @param {String} key (optional) The key associated with the removed item.
12808          */
12809         "remove" : true,
12810         "sort" : true
12811     });
12812     this.allowFunctions = allowFunctions === true;
12813     if(keyFn){
12814         this.getKey = keyFn;
12815     }
12816     Roo.util.MixedCollection.superclass.constructor.call(this);
12817 };
12818
12819 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12820     allowFunctions : false,
12821     
12822 /**
12823  * Adds an item to the collection.
12824  * @param {String} key The key to associate with the item
12825  * @param {Object} o The item to add.
12826  * @return {Object} The item added.
12827  */
12828     add : function(key, o){
12829         if(arguments.length == 1){
12830             o = arguments[0];
12831             key = this.getKey(o);
12832         }
12833         if(typeof key == "undefined" || key === null){
12834             this.length++;
12835             this.items.push(o);
12836             this.keys.push(null);
12837         }else{
12838             var old = this.map[key];
12839             if(old){
12840                 return this.replace(key, o);
12841             }
12842             this.length++;
12843             this.items.push(o);
12844             this.map[key] = o;
12845             this.keys.push(key);
12846         }
12847         this.fireEvent("add", this.length-1, o, key);
12848         return o;
12849     },
12850        
12851 /**
12852   * MixedCollection has a generic way to fetch keys if you implement getKey.
12853 <pre><code>
12854 // normal way
12855 var mc = new Roo.util.MixedCollection();
12856 mc.add(someEl.dom.id, someEl);
12857 mc.add(otherEl.dom.id, otherEl);
12858 //and so on
12859
12860 // using getKey
12861 var mc = new Roo.util.MixedCollection();
12862 mc.getKey = function(el){
12863    return el.dom.id;
12864 };
12865 mc.add(someEl);
12866 mc.add(otherEl);
12867
12868 // or via the constructor
12869 var mc = new Roo.util.MixedCollection(false, function(el){
12870    return el.dom.id;
12871 });
12872 mc.add(someEl);
12873 mc.add(otherEl);
12874 </code></pre>
12875  * @param o {Object} The item for which to find the key.
12876  * @return {Object} The key for the passed item.
12877  */
12878     getKey : function(o){
12879          return o.id; 
12880     },
12881    
12882 /**
12883  * Replaces an item in the collection.
12884  * @param {String} key The key associated with the item to replace, or the item to replace.
12885  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12886  * @return {Object}  The new item.
12887  */
12888     replace : function(key, o){
12889         if(arguments.length == 1){
12890             o = arguments[0];
12891             key = this.getKey(o);
12892         }
12893         var old = this.item(key);
12894         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12895              return this.add(key, o);
12896         }
12897         var index = this.indexOfKey(key);
12898         this.items[index] = o;
12899         this.map[key] = o;
12900         this.fireEvent("replace", key, old, o);
12901         return o;
12902     },
12903    
12904 /**
12905  * Adds all elements of an Array or an Object to the collection.
12906  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12907  * an Array of values, each of which are added to the collection.
12908  */
12909     addAll : function(objs){
12910         if(arguments.length > 1 || objs instanceof Array){
12911             var args = arguments.length > 1 ? arguments : objs;
12912             for(var i = 0, len = args.length; i < len; i++){
12913                 this.add(args[i]);
12914             }
12915         }else{
12916             for(var key in objs){
12917                 if(this.allowFunctions || typeof objs[key] != "function"){
12918                     this.add(key, objs[key]);
12919                 }
12920             }
12921         }
12922     },
12923    
12924 /**
12925  * Executes the specified function once for every item in the collection, passing each
12926  * item as the first and only parameter. returning false from the function will stop the iteration.
12927  * @param {Function} fn The function to execute for each item.
12928  * @param {Object} scope (optional) The scope in which to execute the function.
12929  */
12930     each : function(fn, scope){
12931         var items = [].concat(this.items); // each safe for removal
12932         for(var i = 0, len = items.length; i < len; i++){
12933             if(fn.call(scope || items[i], items[i], i, len) === false){
12934                 break;
12935             }
12936         }
12937     },
12938    
12939 /**
12940  * Executes the specified function once for every key in the collection, passing each
12941  * key, and its associated item as the first two parameters.
12942  * @param {Function} fn The function to execute for each item.
12943  * @param {Object} scope (optional) The scope in which to execute the function.
12944  */
12945     eachKey : function(fn, scope){
12946         for(var i = 0, len = this.keys.length; i < len; i++){
12947             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12948         }
12949     },
12950    
12951 /**
12952  * Returns the first item in the collection which elicits a true return value from the
12953  * passed selection function.
12954  * @param {Function} fn The selection function to execute for each item.
12955  * @param {Object} scope (optional) The scope in which to execute the function.
12956  * @return {Object} The first item in the collection which returned true from the selection function.
12957  */
12958     find : function(fn, scope){
12959         for(var i = 0, len = this.items.length; i < len; i++){
12960             if(fn.call(scope || window, this.items[i], this.keys[i])){
12961                 return this.items[i];
12962             }
12963         }
12964         return null;
12965     },
12966    
12967 /**
12968  * Inserts an item at the specified index in the collection.
12969  * @param {Number} index The index to insert the item at.
12970  * @param {String} key The key to associate with the new item, or the item itself.
12971  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12972  * @return {Object} The item inserted.
12973  */
12974     insert : function(index, key, o){
12975         if(arguments.length == 2){
12976             o = arguments[1];
12977             key = this.getKey(o);
12978         }
12979         if(index >= this.length){
12980             return this.add(key, o);
12981         }
12982         this.length++;
12983         this.items.splice(index, 0, o);
12984         if(typeof key != "undefined" && key != null){
12985             this.map[key] = o;
12986         }
12987         this.keys.splice(index, 0, key);
12988         this.fireEvent("add", index, o, key);
12989         return o;
12990     },
12991    
12992 /**
12993  * Removed an item from the collection.
12994  * @param {Object} o The item to remove.
12995  * @return {Object} The item removed.
12996  */
12997     remove : function(o){
12998         return this.removeAt(this.indexOf(o));
12999     },
13000    
13001 /**
13002  * Remove an item from a specified index in the collection.
13003  * @param {Number} index The index within the collection of the item to remove.
13004  */
13005     removeAt : function(index){
13006         if(index < this.length && index >= 0){
13007             this.length--;
13008             var o = this.items[index];
13009             this.items.splice(index, 1);
13010             var key = this.keys[index];
13011             if(typeof key != "undefined"){
13012                 delete this.map[key];
13013             }
13014             this.keys.splice(index, 1);
13015             this.fireEvent("remove", o, key);
13016         }
13017     },
13018    
13019 /**
13020  * Removed an item associated with the passed key fom the collection.
13021  * @param {String} key The key of the item to remove.
13022  */
13023     removeKey : function(key){
13024         return this.removeAt(this.indexOfKey(key));
13025     },
13026    
13027 /**
13028  * Returns the number of items in the collection.
13029  * @return {Number} the number of items in the collection.
13030  */
13031     getCount : function(){
13032         return this.length; 
13033     },
13034    
13035 /**
13036  * Returns index within the collection of the passed Object.
13037  * @param {Object} o The item to find the index of.
13038  * @return {Number} index of the item.
13039  */
13040     indexOf : function(o){
13041         if(!this.items.indexOf){
13042             for(var i = 0, len = this.items.length; i < len; i++){
13043                 if(this.items[i] == o) return i;
13044             }
13045             return -1;
13046         }else{
13047             return this.items.indexOf(o);
13048         }
13049     },
13050    
13051 /**
13052  * Returns index within the collection of the passed key.
13053  * @param {String} key The key to find the index of.
13054  * @return {Number} index of the key.
13055  */
13056     indexOfKey : function(key){
13057         if(!this.keys.indexOf){
13058             for(var i = 0, len = this.keys.length; i < len; i++){
13059                 if(this.keys[i] == key) return i;
13060             }
13061             return -1;
13062         }else{
13063             return this.keys.indexOf(key);
13064         }
13065     },
13066    
13067 /**
13068  * Returns the item associated with the passed key OR index. Key has priority over index.
13069  * @param {String/Number} key The key or index of the item.
13070  * @return {Object} The item associated with the passed key.
13071  */
13072     item : function(key){
13073         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13074         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13075     },
13076     
13077 /**
13078  * Returns the item at the specified index.
13079  * @param {Number} index The index of the item.
13080  * @return {Object}
13081  */
13082     itemAt : function(index){
13083         return this.items[index];
13084     },
13085     
13086 /**
13087  * Returns the item associated with the passed key.
13088  * @param {String/Number} key The key of the item.
13089  * @return {Object} The item associated with the passed key.
13090  */
13091     key : function(key){
13092         return this.map[key];
13093     },
13094    
13095 /**
13096  * Returns true if the collection contains the passed Object as an item.
13097  * @param {Object} o  The Object to look for in the collection.
13098  * @return {Boolean} True if the collection contains the Object as an item.
13099  */
13100     contains : function(o){
13101         return this.indexOf(o) != -1;
13102     },
13103    
13104 /**
13105  * Returns true if the collection contains the passed Object as a key.
13106  * @param {String} key The key to look for in the collection.
13107  * @return {Boolean} True if the collection contains the Object as a key.
13108  */
13109     containsKey : function(key){
13110         return typeof this.map[key] != "undefined";
13111     },
13112    
13113 /**
13114  * Removes all items from the collection.
13115  */
13116     clear : function(){
13117         this.length = 0;
13118         this.items = [];
13119         this.keys = [];
13120         this.map = {};
13121         this.fireEvent("clear");
13122     },
13123    
13124 /**
13125  * Returns the first item in the collection.
13126  * @return {Object} the first item in the collection..
13127  */
13128     first : function(){
13129         return this.items[0]; 
13130     },
13131    
13132 /**
13133  * Returns the last item in the collection.
13134  * @return {Object} the last item in the collection..
13135  */
13136     last : function(){
13137         return this.items[this.length-1];   
13138     },
13139     
13140     _sort : function(property, dir, fn){
13141         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13142         fn = fn || function(a, b){
13143             return a-b;
13144         };
13145         var c = [], k = this.keys, items = this.items;
13146         for(var i = 0, len = items.length; i < len; i++){
13147             c[c.length] = {key: k[i], value: items[i], index: i};
13148         }
13149         c.sort(function(a, b){
13150             var v = fn(a[property], b[property]) * dsc;
13151             if(v == 0){
13152                 v = (a.index < b.index ? -1 : 1);
13153             }
13154             return v;
13155         });
13156         for(var i = 0, len = c.length; i < len; i++){
13157             items[i] = c[i].value;
13158             k[i] = c[i].key;
13159         }
13160         this.fireEvent("sort", this);
13161     },
13162     
13163     /**
13164      * Sorts this collection with the passed comparison function
13165      * @param {String} direction (optional) "ASC" or "DESC"
13166      * @param {Function} fn (optional) comparison function
13167      */
13168     sort : function(dir, fn){
13169         this._sort("value", dir, fn);
13170     },
13171     
13172     /**
13173      * Sorts this collection by keys
13174      * @param {String} direction (optional) "ASC" or "DESC"
13175      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13176      */
13177     keySort : function(dir, fn){
13178         this._sort("key", dir, fn || function(a, b){
13179             return String(a).toUpperCase()-String(b).toUpperCase();
13180         });
13181     },
13182     
13183     /**
13184      * Returns a range of items in this collection
13185      * @param {Number} startIndex (optional) defaults to 0
13186      * @param {Number} endIndex (optional) default to the last item
13187      * @return {Array} An array of items
13188      */
13189     getRange : function(start, end){
13190         var items = this.items;
13191         if(items.length < 1){
13192             return [];
13193         }
13194         start = start || 0;
13195         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13196         var r = [];
13197         if(start <= end){
13198             for(var i = start; i <= end; i++) {
13199                     r[r.length] = items[i];
13200             }
13201         }else{
13202             for(var i = start; i >= end; i--) {
13203                     r[r.length] = items[i];
13204             }
13205         }
13206         return r;
13207     },
13208         
13209     /**
13210      * Filter the <i>objects</i> in this collection by a specific property. 
13211      * Returns a new collection that has been filtered.
13212      * @param {String} property A property on your objects
13213      * @param {String/RegExp} value Either string that the property values 
13214      * should start with or a RegExp to test against the property
13215      * @return {MixedCollection} The new filtered collection
13216      */
13217     filter : function(property, value){
13218         if(!value.exec){ // not a regex
13219             value = String(value);
13220             if(value.length == 0){
13221                 return this.clone();
13222             }
13223             value = new RegExp("^" + Roo.escapeRe(value), "i");
13224         }
13225         return this.filterBy(function(o){
13226             return o && value.test(o[property]);
13227         });
13228         },
13229     
13230     /**
13231      * Filter by a function. * Returns a new collection that has been filtered.
13232      * The passed function will be called with each 
13233      * object in the collection. If the function returns true, the value is included 
13234      * otherwise it is filtered.
13235      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13236      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13237      * @return {MixedCollection} The new filtered collection
13238      */
13239     filterBy : function(fn, scope){
13240         var r = new Roo.util.MixedCollection();
13241         r.getKey = this.getKey;
13242         var k = this.keys, it = this.items;
13243         for(var i = 0, len = it.length; i < len; i++){
13244             if(fn.call(scope||this, it[i], k[i])){
13245                                 r.add(k[i], it[i]);
13246                         }
13247         }
13248         return r;
13249     },
13250     
13251     /**
13252      * Creates a duplicate of this collection
13253      * @return {MixedCollection}
13254      */
13255     clone : function(){
13256         var r = new Roo.util.MixedCollection();
13257         var k = this.keys, it = this.items;
13258         for(var i = 0, len = it.length; i < len; i++){
13259             r.add(k[i], it[i]);
13260         }
13261         r.getKey = this.getKey;
13262         return r;
13263     }
13264 });
13265 /**
13266  * Returns the item associated with the passed key or index.
13267  * @method
13268  * @param {String/Number} key The key or index of the item.
13269  * @return {Object} The item associated with the passed key.
13270  */
13271 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13272  * Based on:
13273  * Ext JS Library 1.1.1
13274  * Copyright(c) 2006-2007, Ext JS, LLC.
13275  *
13276  * Originally Released Under LGPL - original licence link has changed is not relivant.
13277  *
13278  * Fork - LGPL
13279  * <script type="text/javascript">
13280  */
13281 /**
13282  * @class Roo.util.JSON
13283  * Modified version of Douglas Crockford"s json.js that doesn"t
13284  * mess with the Object prototype 
13285  * http://www.json.org/js.html
13286  * @singleton
13287  */
13288 Roo.util.JSON = new (function(){
13289     var useHasOwn = {}.hasOwnProperty ? true : false;
13290     
13291     // crashes Safari in some instances
13292     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13293     
13294     var pad = function(n) {
13295         return n < 10 ? "0" + n : n;
13296     };
13297     
13298     var m = {
13299         "\b": '\\b',
13300         "\t": '\\t',
13301         "\n": '\\n',
13302         "\f": '\\f',
13303         "\r": '\\r',
13304         '"' : '\\"',
13305         "\\": '\\\\'
13306     };
13307
13308     var encodeString = function(s){
13309         if (/["\\\x00-\x1f]/.test(s)) {
13310             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13311                 var c = m[b];
13312                 if(c){
13313                     return c;
13314                 }
13315                 c = b.charCodeAt();
13316                 return "\\u00" +
13317                     Math.floor(c / 16).toString(16) +
13318                     (c % 16).toString(16);
13319             }) + '"';
13320         }
13321         return '"' + s + '"';
13322     };
13323     
13324     var encodeArray = function(o){
13325         var a = ["["], b, i, l = o.length, v;
13326             for (i = 0; i < l; i += 1) {
13327                 v = o[i];
13328                 switch (typeof v) {
13329                     case "undefined":
13330                     case "function":
13331                     case "unknown":
13332                         break;
13333                     default:
13334                         if (b) {
13335                             a.push(',');
13336                         }
13337                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13338                         b = true;
13339                 }
13340             }
13341             a.push("]");
13342             return a.join("");
13343     };
13344     
13345     var encodeDate = function(o){
13346         return '"' + o.getFullYear() + "-" +
13347                 pad(o.getMonth() + 1) + "-" +
13348                 pad(o.getDate()) + "T" +
13349                 pad(o.getHours()) + ":" +
13350                 pad(o.getMinutes()) + ":" +
13351                 pad(o.getSeconds()) + '"';
13352     };
13353     
13354     /**
13355      * Encodes an Object, Array or other value
13356      * @param {Mixed} o The variable to encode
13357      * @return {String} The JSON string
13358      */
13359     this.encode = function(o)
13360     {
13361         // should this be extended to fully wrap stringify..
13362         
13363         if(typeof o == "undefined" || o === null){
13364             return "null";
13365         }else if(o instanceof Array){
13366             return encodeArray(o);
13367         }else if(o instanceof Date){
13368             return encodeDate(o);
13369         }else if(typeof o == "string"){
13370             return encodeString(o);
13371         }else if(typeof o == "number"){
13372             return isFinite(o) ? String(o) : "null";
13373         }else if(typeof o == "boolean"){
13374             return String(o);
13375         }else {
13376             var a = ["{"], b, i, v;
13377             for (i in o) {
13378                 if(!useHasOwn || o.hasOwnProperty(i)) {
13379                     v = o[i];
13380                     switch (typeof v) {
13381                     case "undefined":
13382                     case "function":
13383                     case "unknown":
13384                         break;
13385                     default:
13386                         if(b){
13387                             a.push(',');
13388                         }
13389                         a.push(this.encode(i), ":",
13390                                 v === null ? "null" : this.encode(v));
13391                         b = true;
13392                     }
13393                 }
13394             }
13395             a.push("}");
13396             return a.join("");
13397         }
13398     };
13399     
13400     /**
13401      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13402      * @param {String} json The JSON string
13403      * @return {Object} The resulting object
13404      */
13405     this.decode = function(json){
13406         
13407         return  /** eval:var:json */ eval("(" + json + ')');
13408     };
13409 })();
13410 /** 
13411  * Shorthand for {@link Roo.util.JSON#encode}
13412  * @member Roo encode 
13413  * @method */
13414 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13415 /** 
13416  * Shorthand for {@link Roo.util.JSON#decode}
13417  * @member Roo decode 
13418  * @method */
13419 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13420 /*
13421  * Based on:
13422  * Ext JS Library 1.1.1
13423  * Copyright(c) 2006-2007, Ext JS, LLC.
13424  *
13425  * Originally Released Under LGPL - original licence link has changed is not relivant.
13426  *
13427  * Fork - LGPL
13428  * <script type="text/javascript">
13429  */
13430  
13431 /**
13432  * @class Roo.util.Format
13433  * Reusable data formatting functions
13434  * @singleton
13435  */
13436 Roo.util.Format = function(){
13437     var trimRe = /^\s+|\s+$/g;
13438     return {
13439         /**
13440          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13441          * @param {String} value The string to truncate
13442          * @param {Number} length The maximum length to allow before truncating
13443          * @return {String} The converted text
13444          */
13445         ellipsis : function(value, len){
13446             if(value && value.length > len){
13447                 return value.substr(0, len-3)+"...";
13448             }
13449             return value;
13450         },
13451
13452         /**
13453          * Checks a reference and converts it to empty string if it is undefined
13454          * @param {Mixed} value Reference to check
13455          * @return {Mixed} Empty string if converted, otherwise the original value
13456          */
13457         undef : function(value){
13458             return typeof value != "undefined" ? value : "";
13459         },
13460
13461         /**
13462          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13463          * @param {String} value The string to encode
13464          * @return {String} The encoded text
13465          */
13466         htmlEncode : function(value){
13467             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13468         },
13469
13470         /**
13471          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13472          * @param {String} value The string to decode
13473          * @return {String} The decoded text
13474          */
13475         htmlDecode : function(value){
13476             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13477         },
13478
13479         /**
13480          * Trims any whitespace from either side of a string
13481          * @param {String} value The text to trim
13482          * @return {String} The trimmed text
13483          */
13484         trim : function(value){
13485             return String(value).replace(trimRe, "");
13486         },
13487
13488         /**
13489          * Returns a substring from within an original string
13490          * @param {String} value The original text
13491          * @param {Number} start The start index of the substring
13492          * @param {Number} length The length of the substring
13493          * @return {String} The substring
13494          */
13495         substr : function(value, start, length){
13496             return String(value).substr(start, length);
13497         },
13498
13499         /**
13500          * Converts a string to all lower case letters
13501          * @param {String} value The text to convert
13502          * @return {String} The converted text
13503          */
13504         lowercase : function(value){
13505             return String(value).toLowerCase();
13506         },
13507
13508         /**
13509          * Converts a string to all upper case letters
13510          * @param {String} value The text to convert
13511          * @return {String} The converted text
13512          */
13513         uppercase : function(value){
13514             return String(value).toUpperCase();
13515         },
13516
13517         /**
13518          * Converts the first character only of a string to upper case
13519          * @param {String} value The text to convert
13520          * @return {String} The converted text
13521          */
13522         capitalize : function(value){
13523             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13524         },
13525
13526         // private
13527         call : function(value, fn){
13528             if(arguments.length > 2){
13529                 var args = Array.prototype.slice.call(arguments, 2);
13530                 args.unshift(value);
13531                  
13532                 return /** eval:var:value */  eval(fn).apply(window, args);
13533             }else{
13534                 /** eval:var:value */
13535                 return /** eval:var:value */ eval(fn).call(window, value);
13536             }
13537         },
13538
13539        
13540         /**
13541          * safer version of Math.toFixed..??/
13542          * @param {Number/String} value The numeric value to format
13543          * @param {Number/String} value Decimal places 
13544          * @return {String} The formatted currency string
13545          */
13546         toFixed : function(v, n)
13547         {
13548             // why not use to fixed - precision is buggered???
13549             if (!n) {
13550                 return Math.round(v-0);
13551             }
13552             var fact = Math.pow(10,n+1);
13553             v = (Math.round((v-0)*fact))/fact;
13554             var z = (''+fact).substring(2);
13555             if (v == Math.floor(v)) {
13556                 return Math.floor(v) + '.' + z;
13557             }
13558             
13559             // now just padd decimals..
13560             var ps = String(v).split('.');
13561             var fd = (ps[1] + z);
13562             var r = fd.substring(0,n); 
13563             var rm = fd.substring(n); 
13564             if (rm < 5) {
13565                 return ps[0] + '.' + r;
13566             }
13567             r*=1; // turn it into a number;
13568             r++;
13569             if (String(r).length != n) {
13570                 ps[0]*=1;
13571                 ps[0]++;
13572                 r = String(r).substring(1); // chop the end off.
13573             }
13574             
13575             return ps[0] + '.' + r;
13576              
13577         },
13578         
13579         /**
13580          * Format a number as US currency
13581          * @param {Number/String} value The numeric value to format
13582          * @return {String} The formatted currency string
13583          */
13584         usMoney : function(v){
13585             return '$' + this.number(v);
13586         },
13587         
13588         /**
13589          * Format a number
13590          * eventually this should probably emulate php's number_format
13591          * @param {Number/String} value The numeric value to format
13592          * @param {Number} decimals number of decimal places
13593          * @return {String} The formatted currency string
13594          */
13595         number : function(v,decimals)
13596         {
13597             // multiply and round.
13598             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13599             var mul = Math.pow(10, decimals);
13600             var zero = String(mul).substring(1);
13601             v = (Math.round((v-0)*mul))/mul;
13602             
13603             // if it's '0' number.. then
13604             
13605             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13606             v = String(v);
13607             var ps = v.split('.');
13608             var whole = ps[0];
13609             
13610             
13611             var r = /(\d+)(\d{3})/;
13612             // add comma's
13613             while (r.test(whole)) {
13614                 whole = whole.replace(r, '$1' + ',' + '$2');
13615             }
13616             
13617             
13618             var sub = ps[1] ?
13619                     (decimals ?  ('.'+ zero.substring(ps[1].length) + ps[1]) : '') :
13620                     (decimals ? ('.' + zero) : '');
13621             
13622             
13623             return whole + sub ;
13624         },
13625         
13626         /**
13627          * Parse a value into a formatted date using the specified format pattern.
13628          * @param {Mixed} value The value to format
13629          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13630          * @return {String} The formatted date string
13631          */
13632         date : function(v, format){
13633             if(!v){
13634                 return "";
13635             }
13636             if(!(v instanceof Date)){
13637                 v = new Date(Date.parse(v));
13638             }
13639             return v.dateFormat(format || "m/d/Y");
13640         },
13641
13642         /**
13643          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13644          * @param {String} format Any valid date format string
13645          * @return {Function} The date formatting function
13646          */
13647         dateRenderer : function(format){
13648             return function(v){
13649                 return Roo.util.Format.date(v, format);  
13650             };
13651         },
13652
13653         // private
13654         stripTagsRE : /<\/?[^>]+>/gi,
13655         
13656         /**
13657          * Strips all HTML tags
13658          * @param {Mixed} value The text from which to strip tags
13659          * @return {String} The stripped text
13660          */
13661         stripTags : function(v){
13662             return !v ? v : String(v).replace(this.stripTagsRE, "");
13663         }
13664     };
13665 }();/*
13666  * Based on:
13667  * Ext JS Library 1.1.1
13668  * Copyright(c) 2006-2007, Ext JS, LLC.
13669  *
13670  * Originally Released Under LGPL - original licence link has changed is not relivant.
13671  *
13672  * Fork - LGPL
13673  * <script type="text/javascript">
13674  */
13675
13676
13677  
13678
13679 /**
13680  * @class Roo.MasterTemplate
13681  * @extends Roo.Template
13682  * Provides a template that can have child templates. The syntax is:
13683 <pre><code>
13684 var t = new Roo.MasterTemplate(
13685         '&lt;select name="{name}"&gt;',
13686                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13687         '&lt;/select&gt;'
13688 );
13689 t.add('options', {value: 'foo', text: 'bar'});
13690 // or you can add multiple child elements in one shot
13691 t.addAll('options', [
13692     {value: 'foo', text: 'bar'},
13693     {value: 'foo2', text: 'bar2'},
13694     {value: 'foo3', text: 'bar3'}
13695 ]);
13696 // then append, applying the master template values
13697 t.append('my-form', {name: 'my-select'});
13698 </code></pre>
13699 * A name attribute for the child template is not required if you have only one child
13700 * template or you want to refer to them by index.
13701  */
13702 Roo.MasterTemplate = function(){
13703     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13704     this.originalHtml = this.html;
13705     var st = {};
13706     var m, re = this.subTemplateRe;
13707     re.lastIndex = 0;
13708     var subIndex = 0;
13709     while(m = re.exec(this.html)){
13710         var name = m[1], content = m[2];
13711         st[subIndex] = {
13712             name: name,
13713             index: subIndex,
13714             buffer: [],
13715             tpl : new Roo.Template(content)
13716         };
13717         if(name){
13718             st[name] = st[subIndex];
13719         }
13720         st[subIndex].tpl.compile();
13721         st[subIndex].tpl.call = this.call.createDelegate(this);
13722         subIndex++;
13723     }
13724     this.subCount = subIndex;
13725     this.subs = st;
13726 };
13727 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13728     /**
13729     * The regular expression used to match sub templates
13730     * @type RegExp
13731     * @property
13732     */
13733     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13734
13735     /**
13736      * Applies the passed values to a child template.
13737      * @param {String/Number} name (optional) The name or index of the child template
13738      * @param {Array/Object} values The values to be applied to the template
13739      * @return {MasterTemplate} this
13740      */
13741      add : function(name, values){
13742         if(arguments.length == 1){
13743             values = arguments[0];
13744             name = 0;
13745         }
13746         var s = this.subs[name];
13747         s.buffer[s.buffer.length] = s.tpl.apply(values);
13748         return this;
13749     },
13750
13751     /**
13752      * Applies all the passed values to a child template.
13753      * @param {String/Number} name (optional) The name or index of the child template
13754      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13755      * @param {Boolean} reset (optional) True to reset the template first
13756      * @return {MasterTemplate} this
13757      */
13758     fill : function(name, values, reset){
13759         var a = arguments;
13760         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13761             values = a[0];
13762             name = 0;
13763             reset = a[1];
13764         }
13765         if(reset){
13766             this.reset();
13767         }
13768         for(var i = 0, len = values.length; i < len; i++){
13769             this.add(name, values[i]);
13770         }
13771         return this;
13772     },
13773
13774     /**
13775      * Resets the template for reuse
13776      * @return {MasterTemplate} this
13777      */
13778      reset : function(){
13779         var s = this.subs;
13780         for(var i = 0; i < this.subCount; i++){
13781             s[i].buffer = [];
13782         }
13783         return this;
13784     },
13785
13786     applyTemplate : function(values){
13787         var s = this.subs;
13788         var replaceIndex = -1;
13789         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13790             return s[++replaceIndex].buffer.join("");
13791         });
13792         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13793     },
13794
13795     apply : function(){
13796         return this.applyTemplate.apply(this, arguments);
13797     },
13798
13799     compile : function(){return this;}
13800 });
13801
13802 /**
13803  * Alias for fill().
13804  * @method
13805  */
13806 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13807  /**
13808  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13809  * var tpl = Roo.MasterTemplate.from('element-id');
13810  * @param {String/HTMLElement} el
13811  * @param {Object} config
13812  * @static
13813  */
13814 Roo.MasterTemplate.from = function(el, config){
13815     el = Roo.getDom(el);
13816     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13817 };/*
13818  * Based on:
13819  * Ext JS Library 1.1.1
13820  * Copyright(c) 2006-2007, Ext JS, LLC.
13821  *
13822  * Originally Released Under LGPL - original licence link has changed is not relivant.
13823  *
13824  * Fork - LGPL
13825  * <script type="text/javascript">
13826  */
13827
13828  
13829 /**
13830  * @class Roo.util.CSS
13831  * Utility class for manipulating CSS rules
13832  * @singleton
13833  */
13834 Roo.util.CSS = function(){
13835         var rules = null;
13836         var doc = document;
13837
13838     var camelRe = /(-[a-z])/gi;
13839     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13840
13841    return {
13842    /**
13843     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13844     * tag and appended to the HEAD of the document.
13845     * @param {String|Object} cssText The text containing the css rules
13846     * @param {String} id An id to add to the stylesheet for later removal
13847     * @return {StyleSheet}
13848     */
13849     createStyleSheet : function(cssText, id){
13850         var ss;
13851         var head = doc.getElementsByTagName("head")[0];
13852         var nrules = doc.createElement("style");
13853         nrules.setAttribute("type", "text/css");
13854         if(id){
13855             nrules.setAttribute("id", id);
13856         }
13857         if (typeof(cssText) != 'string') {
13858             // support object maps..
13859             // not sure if this a good idea.. 
13860             // perhaps it should be merged with the general css handling
13861             // and handle js style props.
13862             var cssTextNew = [];
13863             for(var n in cssText) {
13864                 var citems = [];
13865                 for(var k in cssText[n]) {
13866                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13867                 }
13868                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13869                 
13870             }
13871             cssText = cssTextNew.join("\n");
13872             
13873         }
13874        
13875        
13876        if(Roo.isIE){
13877            head.appendChild(nrules);
13878            ss = nrules.styleSheet;
13879            ss.cssText = cssText;
13880        }else{
13881            try{
13882                 nrules.appendChild(doc.createTextNode(cssText));
13883            }catch(e){
13884                nrules.cssText = cssText; 
13885            }
13886            head.appendChild(nrules);
13887            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13888        }
13889        this.cacheStyleSheet(ss);
13890        return ss;
13891    },
13892
13893    /**
13894     * Removes a style or link tag by id
13895     * @param {String} id The id of the tag
13896     */
13897    removeStyleSheet : function(id){
13898        var existing = doc.getElementById(id);
13899        if(existing){
13900            existing.parentNode.removeChild(existing);
13901        }
13902    },
13903
13904    /**
13905     * Dynamically swaps an existing stylesheet reference for a new one
13906     * @param {String} id The id of an existing link tag to remove
13907     * @param {String} url The href of the new stylesheet to include
13908     */
13909    swapStyleSheet : function(id, url){
13910        this.removeStyleSheet(id);
13911        var ss = doc.createElement("link");
13912        ss.setAttribute("rel", "stylesheet");
13913        ss.setAttribute("type", "text/css");
13914        ss.setAttribute("id", id);
13915        ss.setAttribute("href", url);
13916        doc.getElementsByTagName("head")[0].appendChild(ss);
13917    },
13918    
13919    /**
13920     * Refresh the rule cache if you have dynamically added stylesheets
13921     * @return {Object} An object (hash) of rules indexed by selector
13922     */
13923    refreshCache : function(){
13924        return this.getRules(true);
13925    },
13926
13927    // private
13928    cacheStyleSheet : function(stylesheet){
13929        if(!rules){
13930            rules = {};
13931        }
13932        try{// try catch for cross domain access issue
13933            var ssRules = stylesheet.cssRules || stylesheet.rules;
13934            for(var j = ssRules.length-1; j >= 0; --j){
13935                rules[ssRules[j].selectorText] = ssRules[j];
13936            }
13937        }catch(e){}
13938    },
13939    
13940    /**
13941     * Gets all css rules for the document
13942     * @param {Boolean} refreshCache true to refresh the internal cache
13943     * @return {Object} An object (hash) of rules indexed by selector
13944     */
13945    getRules : function(refreshCache){
13946                 if(rules == null || refreshCache){
13947                         rules = {};
13948                         var ds = doc.styleSheets;
13949                         for(var i =0, len = ds.length; i < len; i++){
13950                             try{
13951                         this.cacheStyleSheet(ds[i]);
13952                     }catch(e){} 
13953                 }
13954                 }
13955                 return rules;
13956         },
13957         
13958         /**
13959     * Gets an an individual CSS rule by selector(s)
13960     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13961     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13962     * @return {CSSRule} The CSS rule or null if one is not found
13963     */
13964    getRule : function(selector, refreshCache){
13965                 var rs = this.getRules(refreshCache);
13966                 if(!(selector instanceof Array)){
13967                     return rs[selector];
13968                 }
13969                 for(var i = 0; i < selector.length; i++){
13970                         if(rs[selector[i]]){
13971                                 return rs[selector[i]];
13972                         }
13973                 }
13974                 return null;
13975         },
13976         
13977         
13978         /**
13979     * Updates a rule property
13980     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13981     * @param {String} property The css property
13982     * @param {String} value The new value for the property
13983     * @return {Boolean} true If a rule was found and updated
13984     */
13985    updateRule : function(selector, property, value){
13986                 if(!(selector instanceof Array)){
13987                         var rule = this.getRule(selector);
13988                         if(rule){
13989                                 rule.style[property.replace(camelRe, camelFn)] = value;
13990                                 return true;
13991                         }
13992                 }else{
13993                         for(var i = 0; i < selector.length; i++){
13994                                 if(this.updateRule(selector[i], property, value)){
13995                                         return true;
13996                                 }
13997                         }
13998                 }
13999                 return false;
14000         }
14001    };   
14002 }();/*
14003  * Based on:
14004  * Ext JS Library 1.1.1
14005  * Copyright(c) 2006-2007, Ext JS, LLC.
14006  *
14007  * Originally Released Under LGPL - original licence link has changed is not relivant.
14008  *
14009  * Fork - LGPL
14010  * <script type="text/javascript">
14011  */
14012
14013  
14014
14015 /**
14016  * @class Roo.util.ClickRepeater
14017  * @extends Roo.util.Observable
14018  * 
14019  * A wrapper class which can be applied to any element. Fires a "click" event while the
14020  * mouse is pressed. The interval between firings may be specified in the config but
14021  * defaults to 10 milliseconds.
14022  * 
14023  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14024  * 
14025  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14026  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14027  * Similar to an autorepeat key delay.
14028  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14029  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14030  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14031  *           "interval" and "delay" are ignored. "immediate" is honored.
14032  * @cfg {Boolean} preventDefault True to prevent the default click event
14033  * @cfg {Boolean} stopDefault True to stop the default click event
14034  * 
14035  * @history
14036  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14037  *     2007-02-02 jvs Renamed to ClickRepeater
14038  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14039  *
14040  *  @constructor
14041  * @param {String/HTMLElement/Element} el The element to listen on
14042  * @param {Object} config
14043  **/
14044 Roo.util.ClickRepeater = function(el, config)
14045 {
14046     this.el = Roo.get(el);
14047     this.el.unselectable();
14048
14049     Roo.apply(this, config);
14050
14051     this.addEvents({
14052     /**
14053      * @event mousedown
14054      * Fires when the mouse button is depressed.
14055      * @param {Roo.util.ClickRepeater} this
14056      */
14057         "mousedown" : true,
14058     /**
14059      * @event click
14060      * Fires on a specified interval during the time the element is pressed.
14061      * @param {Roo.util.ClickRepeater} this
14062      */
14063         "click" : true,
14064     /**
14065      * @event mouseup
14066      * Fires when the mouse key is released.
14067      * @param {Roo.util.ClickRepeater} this
14068      */
14069         "mouseup" : true
14070     });
14071
14072     this.el.on("mousedown", this.handleMouseDown, this);
14073     if(this.preventDefault || this.stopDefault){
14074         this.el.on("click", function(e){
14075             if(this.preventDefault){
14076                 e.preventDefault();
14077             }
14078             if(this.stopDefault){
14079                 e.stopEvent();
14080             }
14081         }, this);
14082     }
14083
14084     // allow inline handler
14085     if(this.handler){
14086         this.on("click", this.handler,  this.scope || this);
14087     }
14088
14089     Roo.util.ClickRepeater.superclass.constructor.call(this);
14090 };
14091
14092 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14093     interval : 20,
14094     delay: 250,
14095     preventDefault : true,
14096     stopDefault : false,
14097     timer : 0,
14098
14099     // private
14100     handleMouseDown : function(){
14101         clearTimeout(this.timer);
14102         this.el.blur();
14103         if(this.pressClass){
14104             this.el.addClass(this.pressClass);
14105         }
14106         this.mousedownTime = new Date();
14107
14108         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14109         this.el.on("mouseout", this.handleMouseOut, this);
14110
14111         this.fireEvent("mousedown", this);
14112         this.fireEvent("click", this);
14113         
14114         this.timer = this.click.defer(this.delay || this.interval, this);
14115     },
14116
14117     // private
14118     click : function(){
14119         this.fireEvent("click", this);
14120         this.timer = this.click.defer(this.getInterval(), this);
14121     },
14122
14123     // private
14124     getInterval: function(){
14125         if(!this.accelerate){
14126             return this.interval;
14127         }
14128         var pressTime = this.mousedownTime.getElapsed();
14129         if(pressTime < 500){
14130             return 400;
14131         }else if(pressTime < 1700){
14132             return 320;
14133         }else if(pressTime < 2600){
14134             return 250;
14135         }else if(pressTime < 3500){
14136             return 180;
14137         }else if(pressTime < 4400){
14138             return 140;
14139         }else if(pressTime < 5300){
14140             return 80;
14141         }else if(pressTime < 6200){
14142             return 50;
14143         }else{
14144             return 10;
14145         }
14146     },
14147
14148     // private
14149     handleMouseOut : function(){
14150         clearTimeout(this.timer);
14151         if(this.pressClass){
14152             this.el.removeClass(this.pressClass);
14153         }
14154         this.el.on("mouseover", this.handleMouseReturn, this);
14155     },
14156
14157     // private
14158     handleMouseReturn : function(){
14159         this.el.un("mouseover", this.handleMouseReturn);
14160         if(this.pressClass){
14161             this.el.addClass(this.pressClass);
14162         }
14163         this.click();
14164     },
14165
14166     // private
14167     handleMouseUp : function(){
14168         clearTimeout(this.timer);
14169         this.el.un("mouseover", this.handleMouseReturn);
14170         this.el.un("mouseout", this.handleMouseOut);
14171         Roo.get(document).un("mouseup", this.handleMouseUp);
14172         this.el.removeClass(this.pressClass);
14173         this.fireEvent("mouseup", this);
14174     }
14175 });/*
14176  * Based on:
14177  * Ext JS Library 1.1.1
14178  * Copyright(c) 2006-2007, Ext JS, LLC.
14179  *
14180  * Originally Released Under LGPL - original licence link has changed is not relivant.
14181  *
14182  * Fork - LGPL
14183  * <script type="text/javascript">
14184  */
14185
14186  
14187 /**
14188  * @class Roo.KeyNav
14189  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14190  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14191  * way to implement custom navigation schemes for any UI component.</p>
14192  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14193  * pageUp, pageDown, del, home, end.  Usage:</p>
14194  <pre><code>
14195 var nav = new Roo.KeyNav("my-element", {
14196     "left" : function(e){
14197         this.moveLeft(e.ctrlKey);
14198     },
14199     "right" : function(e){
14200         this.moveRight(e.ctrlKey);
14201     },
14202     "enter" : function(e){
14203         this.save();
14204     },
14205     scope : this
14206 });
14207 </code></pre>
14208  * @constructor
14209  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14210  * @param {Object} config The config
14211  */
14212 Roo.KeyNav = function(el, config){
14213     this.el = Roo.get(el);
14214     Roo.apply(this, config);
14215     if(!this.disabled){
14216         this.disabled = true;
14217         this.enable();
14218     }
14219 };
14220
14221 Roo.KeyNav.prototype = {
14222     /**
14223      * @cfg {Boolean} disabled
14224      * True to disable this KeyNav instance (defaults to false)
14225      */
14226     disabled : false,
14227     /**
14228      * @cfg {String} defaultEventAction
14229      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14230      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14231      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14232      */
14233     defaultEventAction: "stopEvent",
14234     /**
14235      * @cfg {Boolean} forceKeyDown
14236      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14237      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14238      * handle keydown instead of keypress.
14239      */
14240     forceKeyDown : false,
14241
14242     // private
14243     prepareEvent : function(e){
14244         var k = e.getKey();
14245         var h = this.keyToHandler[k];
14246         //if(h && this[h]){
14247         //    e.stopPropagation();
14248         //}
14249         if(Roo.isSafari && h && k >= 37 && k <= 40){
14250             e.stopEvent();
14251         }
14252     },
14253
14254     // private
14255     relay : function(e){
14256         var k = e.getKey();
14257         var h = this.keyToHandler[k];
14258         if(h && this[h]){
14259             if(this.doRelay(e, this[h], h) !== true){
14260                 e[this.defaultEventAction]();
14261             }
14262         }
14263     },
14264
14265     // private
14266     doRelay : function(e, h, hname){
14267         return h.call(this.scope || this, e);
14268     },
14269
14270     // possible handlers
14271     enter : false,
14272     left : false,
14273     right : false,
14274     up : false,
14275     down : false,
14276     tab : false,
14277     esc : false,
14278     pageUp : false,
14279     pageDown : false,
14280     del : false,
14281     home : false,
14282     end : false,
14283
14284     // quick lookup hash
14285     keyToHandler : {
14286         37 : "left",
14287         39 : "right",
14288         38 : "up",
14289         40 : "down",
14290         33 : "pageUp",
14291         34 : "pageDown",
14292         46 : "del",
14293         36 : "home",
14294         35 : "end",
14295         13 : "enter",
14296         27 : "esc",
14297         9  : "tab"
14298     },
14299
14300         /**
14301          * Enable this KeyNav
14302          */
14303         enable: function(){
14304                 if(this.disabled){
14305             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14306             // the EventObject will normalize Safari automatically
14307             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14308                 this.el.on("keydown", this.relay,  this);
14309             }else{
14310                 this.el.on("keydown", this.prepareEvent,  this);
14311                 this.el.on("keypress", this.relay,  this);
14312             }
14313                     this.disabled = false;
14314                 }
14315         },
14316
14317         /**
14318          * Disable this KeyNav
14319          */
14320         disable: function(){
14321                 if(!this.disabled){
14322                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14323                 this.el.un("keydown", this.relay);
14324             }else{
14325                 this.el.un("keydown", this.prepareEvent);
14326                 this.el.un("keypress", this.relay);
14327             }
14328                     this.disabled = true;
14329                 }
14330         }
14331 };/*
14332  * Based on:
14333  * Ext JS Library 1.1.1
14334  * Copyright(c) 2006-2007, Ext JS, LLC.
14335  *
14336  * Originally Released Under LGPL - original licence link has changed is not relivant.
14337  *
14338  * Fork - LGPL
14339  * <script type="text/javascript">
14340  */
14341
14342  
14343 /**
14344  * @class Roo.KeyMap
14345  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14346  * The constructor accepts the same config object as defined by {@link #addBinding}.
14347  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14348  * combination it will call the function with this signature (if the match is a multi-key
14349  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14350  * A KeyMap can also handle a string representation of keys.<br />
14351  * Usage:
14352  <pre><code>
14353 // map one key by key code
14354 var map = new Roo.KeyMap("my-element", {
14355     key: 13, // or Roo.EventObject.ENTER
14356     fn: myHandler,
14357     scope: myObject
14358 });
14359
14360 // map multiple keys to one action by string
14361 var map = new Roo.KeyMap("my-element", {
14362     key: "a\r\n\t",
14363     fn: myHandler,
14364     scope: myObject
14365 });
14366
14367 // map multiple keys to multiple actions by strings and array of codes
14368 var map = new Roo.KeyMap("my-element", [
14369     {
14370         key: [10,13],
14371         fn: function(){ alert("Return was pressed"); }
14372     }, {
14373         key: "abc",
14374         fn: function(){ alert('a, b or c was pressed'); }
14375     }, {
14376         key: "\t",
14377         ctrl:true,
14378         shift:true,
14379         fn: function(){ alert('Control + shift + tab was pressed.'); }
14380     }
14381 ]);
14382 </code></pre>
14383  * <b>Note: A KeyMap starts enabled</b>
14384  * @constructor
14385  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14386  * @param {Object} config The config (see {@link #addBinding})
14387  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14388  */
14389 Roo.KeyMap = function(el, config, eventName){
14390     this.el  = Roo.get(el);
14391     this.eventName = eventName || "keydown";
14392     this.bindings = [];
14393     if(config){
14394         this.addBinding(config);
14395     }
14396     this.enable();
14397 };
14398
14399 Roo.KeyMap.prototype = {
14400     /**
14401      * True to stop the event from bubbling and prevent the default browser action if the
14402      * key was handled by the KeyMap (defaults to false)
14403      * @type Boolean
14404      */
14405     stopEvent : false,
14406
14407     /**
14408      * Add a new binding to this KeyMap. The following config object properties are supported:
14409      * <pre>
14410 Property    Type             Description
14411 ----------  ---------------  ----------------------------------------------------------------------
14412 key         String/Array     A single keycode or an array of keycodes to handle
14413 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14414 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14415 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14416 fn          Function         The function to call when KeyMap finds the expected key combination
14417 scope       Object           The scope of the callback function
14418 </pre>
14419      *
14420      * Usage:
14421      * <pre><code>
14422 // Create a KeyMap
14423 var map = new Roo.KeyMap(document, {
14424     key: Roo.EventObject.ENTER,
14425     fn: handleKey,
14426     scope: this
14427 });
14428
14429 //Add a new binding to the existing KeyMap later
14430 map.addBinding({
14431     key: 'abc',
14432     shift: true,
14433     fn: handleKey,
14434     scope: this
14435 });
14436 </code></pre>
14437      * @param {Object/Array} config A single KeyMap config or an array of configs
14438      */
14439         addBinding : function(config){
14440         if(config instanceof Array){
14441             for(var i = 0, len = config.length; i < len; i++){
14442                 this.addBinding(config[i]);
14443             }
14444             return;
14445         }
14446         var keyCode = config.key,
14447             shift = config.shift, 
14448             ctrl = config.ctrl, 
14449             alt = config.alt,
14450             fn = config.fn,
14451             scope = config.scope;
14452         if(typeof keyCode == "string"){
14453             var ks = [];
14454             var keyString = keyCode.toUpperCase();
14455             for(var j = 0, len = keyString.length; j < len; j++){
14456                 ks.push(keyString.charCodeAt(j));
14457             }
14458             keyCode = ks;
14459         }
14460         var keyArray = keyCode instanceof Array;
14461         var handler = function(e){
14462             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14463                 var k = e.getKey();
14464                 if(keyArray){
14465                     for(var i = 0, len = keyCode.length; i < len; i++){
14466                         if(keyCode[i] == k){
14467                           if(this.stopEvent){
14468                               e.stopEvent();
14469                           }
14470                           fn.call(scope || window, k, e);
14471                           return;
14472                         }
14473                     }
14474                 }else{
14475                     if(k == keyCode){
14476                         if(this.stopEvent){
14477                            e.stopEvent();
14478                         }
14479                         fn.call(scope || window, k, e);
14480                     }
14481                 }
14482             }
14483         };
14484         this.bindings.push(handler);  
14485         },
14486
14487     /**
14488      * Shorthand for adding a single key listener
14489      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14490      * following options:
14491      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14492      * @param {Function} fn The function to call
14493      * @param {Object} scope (optional) The scope of the function
14494      */
14495     on : function(key, fn, scope){
14496         var keyCode, shift, ctrl, alt;
14497         if(typeof key == "object" && !(key instanceof Array)){
14498             keyCode = key.key;
14499             shift = key.shift;
14500             ctrl = key.ctrl;
14501             alt = key.alt;
14502         }else{
14503             keyCode = key;
14504         }
14505         this.addBinding({
14506             key: keyCode,
14507             shift: shift,
14508             ctrl: ctrl,
14509             alt: alt,
14510             fn: fn,
14511             scope: scope
14512         })
14513     },
14514
14515     // private
14516     handleKeyDown : function(e){
14517             if(this.enabled){ //just in case
14518             var b = this.bindings;
14519             for(var i = 0, len = b.length; i < len; i++){
14520                 b[i].call(this, e);
14521             }
14522             }
14523         },
14524         
14525         /**
14526          * Returns true if this KeyMap is enabled
14527          * @return {Boolean} 
14528          */
14529         isEnabled : function(){
14530             return this.enabled;  
14531         },
14532         
14533         /**
14534          * Enables this KeyMap
14535          */
14536         enable: function(){
14537                 if(!this.enabled){
14538                     this.el.on(this.eventName, this.handleKeyDown, this);
14539                     this.enabled = true;
14540                 }
14541         },
14542
14543         /**
14544          * Disable this KeyMap
14545          */
14546         disable: function(){
14547                 if(this.enabled){
14548                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14549                     this.enabled = false;
14550                 }
14551         }
14552 };/*
14553  * Based on:
14554  * Ext JS Library 1.1.1
14555  * Copyright(c) 2006-2007, Ext JS, LLC.
14556  *
14557  * Originally Released Under LGPL - original licence link has changed is not relivant.
14558  *
14559  * Fork - LGPL
14560  * <script type="text/javascript">
14561  */
14562
14563  
14564 /**
14565  * @class Roo.util.TextMetrics
14566  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14567  * wide, in pixels, a given block of text will be.
14568  * @singleton
14569  */
14570 Roo.util.TextMetrics = function(){
14571     var shared;
14572     return {
14573         /**
14574          * Measures the size of the specified text
14575          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14576          * that can affect the size of the rendered text
14577          * @param {String} text The text to measure
14578          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14579          * in order to accurately measure the text height
14580          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14581          */
14582         measure : function(el, text, fixedWidth){
14583             if(!shared){
14584                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14585             }
14586             shared.bind(el);
14587             shared.setFixedWidth(fixedWidth || 'auto');
14588             return shared.getSize(text);
14589         },
14590
14591         /**
14592          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14593          * the overhead of multiple calls to initialize the style properties on each measurement.
14594          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14595          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14596          * in order to accurately measure the text height
14597          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14598          */
14599         createInstance : function(el, fixedWidth){
14600             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14601         }
14602     };
14603 }();
14604
14605  
14606
14607 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14608     var ml = new Roo.Element(document.createElement('div'));
14609     document.body.appendChild(ml.dom);
14610     ml.position('absolute');
14611     ml.setLeftTop(-1000, -1000);
14612     ml.hide();
14613
14614     if(fixedWidth){
14615         ml.setWidth(fixedWidth);
14616     }
14617      
14618     var instance = {
14619         /**
14620          * Returns the size of the specified text based on the internal element's style and width properties
14621          * @memberOf Roo.util.TextMetrics.Instance#
14622          * @param {String} text The text to measure
14623          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14624          */
14625         getSize : function(text){
14626             ml.update(text);
14627             var s = ml.getSize();
14628             ml.update('');
14629             return s;
14630         },
14631
14632         /**
14633          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14634          * that can affect the size of the rendered text
14635          * @memberOf Roo.util.TextMetrics.Instance#
14636          * @param {String/HTMLElement} el The element, dom node or id
14637          */
14638         bind : function(el){
14639             ml.setStyle(
14640                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14641             );
14642         },
14643
14644         /**
14645          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14646          * to set a fixed width in order to accurately measure the text height.
14647          * @memberOf Roo.util.TextMetrics.Instance#
14648          * @param {Number} width The width to set on the element
14649          */
14650         setFixedWidth : function(width){
14651             ml.setWidth(width);
14652         },
14653
14654         /**
14655          * Returns the measured width of the specified text
14656          * @memberOf Roo.util.TextMetrics.Instance#
14657          * @param {String} text The text to measure
14658          * @return {Number} width The width in pixels
14659          */
14660         getWidth : function(text){
14661             ml.dom.style.width = 'auto';
14662             return this.getSize(text).width;
14663         },
14664
14665         /**
14666          * Returns the measured height of the specified text.  For multiline text, be sure to call
14667          * {@link #setFixedWidth} if necessary.
14668          * @memberOf Roo.util.TextMetrics.Instance#
14669          * @param {String} text The text to measure
14670          * @return {Number} height The height in pixels
14671          */
14672         getHeight : function(text){
14673             return this.getSize(text).height;
14674         }
14675     };
14676
14677     instance.bind(bindTo);
14678
14679     return instance;
14680 };
14681
14682 // backwards compat
14683 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14684  * Based on:
14685  * Ext JS Library 1.1.1
14686  * Copyright(c) 2006-2007, Ext JS, LLC.
14687  *
14688  * Originally Released Under LGPL - original licence link has changed is not relivant.
14689  *
14690  * Fork - LGPL
14691  * <script type="text/javascript">
14692  */
14693
14694 /**
14695  * @class Roo.state.Provider
14696  * Abstract base class for state provider implementations. This class provides methods
14697  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14698  * Provider interface.
14699  */
14700 Roo.state.Provider = function(){
14701     /**
14702      * @event statechange
14703      * Fires when a state change occurs.
14704      * @param {Provider} this This state provider
14705      * @param {String} key The state key which was changed
14706      * @param {String} value The encoded value for the state
14707      */
14708     this.addEvents({
14709         "statechange": true
14710     });
14711     this.state = {};
14712     Roo.state.Provider.superclass.constructor.call(this);
14713 };
14714 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14715     /**
14716      * Returns the current value for a key
14717      * @param {String} name The key name
14718      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14719      * @return {Mixed} The state data
14720      */
14721     get : function(name, defaultValue){
14722         return typeof this.state[name] == "undefined" ?
14723             defaultValue : this.state[name];
14724     },
14725     
14726     /**
14727      * Clears a value from the state
14728      * @param {String} name The key name
14729      */
14730     clear : function(name){
14731         delete this.state[name];
14732         this.fireEvent("statechange", this, name, null);
14733     },
14734     
14735     /**
14736      * Sets the value for a key
14737      * @param {String} name The key name
14738      * @param {Mixed} value The value to set
14739      */
14740     set : function(name, value){
14741         this.state[name] = value;
14742         this.fireEvent("statechange", this, name, value);
14743     },
14744     
14745     /**
14746      * Decodes a string previously encoded with {@link #encodeValue}.
14747      * @param {String} value The value to decode
14748      * @return {Mixed} The decoded value
14749      */
14750     decodeValue : function(cookie){
14751         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14752         var matches = re.exec(unescape(cookie));
14753         if(!matches || !matches[1]) return; // non state cookie
14754         var type = matches[1];
14755         var v = matches[2];
14756         switch(type){
14757             case "n":
14758                 return parseFloat(v);
14759             case "d":
14760                 return new Date(Date.parse(v));
14761             case "b":
14762                 return (v == "1");
14763             case "a":
14764                 var all = [];
14765                 var values = v.split("^");
14766                 for(var i = 0, len = values.length; i < len; i++){
14767                     all.push(this.decodeValue(values[i]));
14768                 }
14769                 return all;
14770            case "o":
14771                 var all = {};
14772                 var values = v.split("^");
14773                 for(var i = 0, len = values.length; i < len; i++){
14774                     var kv = values[i].split("=");
14775                     all[kv[0]] = this.decodeValue(kv[1]);
14776                 }
14777                 return all;
14778            default:
14779                 return v;
14780         }
14781     },
14782     
14783     /**
14784      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14785      * @param {Mixed} value The value to encode
14786      * @return {String} The encoded value
14787      */
14788     encodeValue : function(v){
14789         var enc;
14790         if(typeof v == "number"){
14791             enc = "n:" + v;
14792         }else if(typeof v == "boolean"){
14793             enc = "b:" + (v ? "1" : "0");
14794         }else if(v instanceof Date){
14795             enc = "d:" + v.toGMTString();
14796         }else if(v instanceof Array){
14797             var flat = "";
14798             for(var i = 0, len = v.length; i < len; i++){
14799                 flat += this.encodeValue(v[i]);
14800                 if(i != len-1) flat += "^";
14801             }
14802             enc = "a:" + flat;
14803         }else if(typeof v == "object"){
14804             var flat = "";
14805             for(var key in v){
14806                 if(typeof v[key] != "function"){
14807                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14808                 }
14809             }
14810             enc = "o:" + flat.substring(0, flat.length-1);
14811         }else{
14812             enc = "s:" + v;
14813         }
14814         return escape(enc);        
14815     }
14816 });
14817
14818 /*
14819  * Based on:
14820  * Ext JS Library 1.1.1
14821  * Copyright(c) 2006-2007, Ext JS, LLC.
14822  *
14823  * Originally Released Under LGPL - original licence link has changed is not relivant.
14824  *
14825  * Fork - LGPL
14826  * <script type="text/javascript">
14827  */
14828 /**
14829  * @class Roo.state.Manager
14830  * This is the global state manager. By default all components that are "state aware" check this class
14831  * for state information if you don't pass them a custom state provider. In order for this class
14832  * to be useful, it must be initialized with a provider when your application initializes.
14833  <pre><code>
14834 // in your initialization function
14835 init : function(){
14836    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14837    ...
14838    // supposed you have a {@link Roo.BorderLayout}
14839    var layout = new Roo.BorderLayout(...);
14840    layout.restoreState();
14841    // or a {Roo.BasicDialog}
14842    var dialog = new Roo.BasicDialog(...);
14843    dialog.restoreState();
14844  </code></pre>
14845  * @singleton
14846  */
14847 Roo.state.Manager = function(){
14848     var provider = new Roo.state.Provider();
14849     
14850     return {
14851         /**
14852          * Configures the default state provider for your application
14853          * @param {Provider} stateProvider The state provider to set
14854          */
14855         setProvider : function(stateProvider){
14856             provider = stateProvider;
14857         },
14858         
14859         /**
14860          * Returns the current value for a key
14861          * @param {String} name The key name
14862          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14863          * @return {Mixed} The state data
14864          */
14865         get : function(key, defaultValue){
14866             return provider.get(key, defaultValue);
14867         },
14868         
14869         /**
14870          * Sets the value for a key
14871          * @param {String} name The key name
14872          * @param {Mixed} value The state data
14873          */
14874          set : function(key, value){
14875             provider.set(key, value);
14876         },
14877         
14878         /**
14879          * Clears a value from the state
14880          * @param {String} name The key name
14881          */
14882         clear : function(key){
14883             provider.clear(key);
14884         },
14885         
14886         /**
14887          * Gets the currently configured state provider
14888          * @return {Provider} The state provider
14889          */
14890         getProvider : function(){
14891             return provider;
14892         }
14893     };
14894 }();
14895 /*
14896  * Based on:
14897  * Ext JS Library 1.1.1
14898  * Copyright(c) 2006-2007, Ext JS, LLC.
14899  *
14900  * Originally Released Under LGPL - original licence link has changed is not relivant.
14901  *
14902  * Fork - LGPL
14903  * <script type="text/javascript">
14904  */
14905 /**
14906  * @class Roo.state.CookieProvider
14907  * @extends Roo.state.Provider
14908  * The default Provider implementation which saves state via cookies.
14909  * <br />Usage:
14910  <pre><code>
14911    var cp = new Roo.state.CookieProvider({
14912        path: "/cgi-bin/",
14913        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14914        domain: "roojs.com"
14915    })
14916    Roo.state.Manager.setProvider(cp);
14917  </code></pre>
14918  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14919  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14920  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14921  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14922  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14923  * domain the page is running on including the 'www' like 'www.roojs.com')
14924  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14925  * @constructor
14926  * Create a new CookieProvider
14927  * @param {Object} config The configuration object
14928  */
14929 Roo.state.CookieProvider = function(config){
14930     Roo.state.CookieProvider.superclass.constructor.call(this);
14931     this.path = "/";
14932     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14933     this.domain = null;
14934     this.secure = false;
14935     Roo.apply(this, config);
14936     this.state = this.readCookies();
14937 };
14938
14939 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14940     // private
14941     set : function(name, value){
14942         if(typeof value == "undefined" || value === null){
14943             this.clear(name);
14944             return;
14945         }
14946         this.setCookie(name, value);
14947         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14948     },
14949
14950     // private
14951     clear : function(name){
14952         this.clearCookie(name);
14953         Roo.state.CookieProvider.superclass.clear.call(this, name);
14954     },
14955
14956     // private
14957     readCookies : function(){
14958         var cookies = {};
14959         var c = document.cookie + ";";
14960         var re = /\s?(.*?)=(.*?);/g;
14961         var matches;
14962         while((matches = re.exec(c)) != null){
14963             var name = matches[1];
14964             var value = matches[2];
14965             if(name && name.substring(0,3) == "ys-"){
14966                 cookies[name.substr(3)] = this.decodeValue(value);
14967             }
14968         }
14969         return cookies;
14970     },
14971
14972     // private
14973     setCookie : function(name, value){
14974         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14975            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14976            ((this.path == null) ? "" : ("; path=" + this.path)) +
14977            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14978            ((this.secure == true) ? "; secure" : "");
14979     },
14980
14981     // private
14982     clearCookie : function(name){
14983         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14984            ((this.path == null) ? "" : ("; path=" + this.path)) +
14985            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14986            ((this.secure == true) ? "; secure" : "");
14987     }
14988 });/*
14989  * Based on:
14990  * Ext JS Library 1.1.1
14991  * Copyright(c) 2006-2007, Ext JS, LLC.
14992  *
14993  * Originally Released Under LGPL - original licence link has changed is not relivant.
14994  *
14995  * Fork - LGPL
14996  * <script type="text/javascript">
14997  */
14998
14999
15000
15001 /*
15002  * These classes are derivatives of the similarly named classes in the YUI Library.
15003  * The original license:
15004  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
15005  * Code licensed under the BSD License:
15006  * http://developer.yahoo.net/yui/license.txt
15007  */
15008
15009 (function() {
15010
15011 var Event=Roo.EventManager;
15012 var Dom=Roo.lib.Dom;
15013
15014 /**
15015  * @class Roo.dd.DragDrop
15016  * @extends Roo.util.Observable
15017  * Defines the interface and base operation of items that that can be
15018  * dragged or can be drop targets.  It was designed to be extended, overriding
15019  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
15020  * Up to three html elements can be associated with a DragDrop instance:
15021  * <ul>
15022  * <li>linked element: the element that is passed into the constructor.
15023  * This is the element which defines the boundaries for interaction with
15024  * other DragDrop objects.</li>
15025  * <li>handle element(s): The drag operation only occurs if the element that
15026  * was clicked matches a handle element.  By default this is the linked
15027  * element, but there are times that you will want only a portion of the
15028  * linked element to initiate the drag operation, and the setHandleElId()
15029  * method provides a way to define this.</li>
15030  * <li>drag element: this represents the element that would be moved along
15031  * with the cursor during a drag operation.  By default, this is the linked
15032  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
15033  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
15034  * </li>
15035  * </ul>
15036  * This class should not be instantiated until the onload event to ensure that
15037  * the associated elements are available.
15038  * The following would define a DragDrop obj that would interact with any
15039  * other DragDrop obj in the "group1" group:
15040  * <pre>
15041  *  dd = new Roo.dd.DragDrop("div1", "group1");
15042  * </pre>
15043  * Since none of the event handlers have been implemented, nothing would
15044  * actually happen if you were to run the code above.  Normally you would
15045  * override this class or one of the default implementations, but you can
15046  * also override the methods you want on an instance of the class...
15047  * <pre>
15048  *  dd.onDragDrop = function(e, id) {
15049  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
15050  *  }
15051  * </pre>
15052  * @constructor
15053  * @param {String} id of the element that is linked to this instance
15054  * @param {String} sGroup the group of related DragDrop objects
15055  * @param {object} config an object containing configurable attributes
15056  *                Valid properties for DragDrop:
15057  *                    padding, isTarget, maintainOffset, primaryButtonOnly
15058  */
15059 Roo.dd.DragDrop = function(id, sGroup, config) {
15060     if (id) {
15061         this.init(id, sGroup, config);
15062     }
15063     
15064 };
15065
15066 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
15067
15068     /**
15069      * The id of the element associated with this object.  This is what we
15070      * refer to as the "linked element" because the size and position of
15071      * this element is used to determine when the drag and drop objects have
15072      * interacted.
15073      * @property id
15074      * @type String
15075      */
15076     id: null,
15077
15078     /**
15079      * Configuration attributes passed into the constructor
15080      * @property config
15081      * @type object
15082      */
15083     config: null,
15084
15085     /**
15086      * The id of the element that will be dragged.  By default this is same
15087      * as the linked element , but could be changed to another element. Ex:
15088      * Roo.dd.DDProxy
15089      * @property dragElId
15090      * @type String
15091      * @private
15092      */
15093     dragElId: null,
15094
15095     /**
15096      * the id of the element that initiates the drag operation.  By default
15097      * this is the linked element, but could be changed to be a child of this
15098      * element.  This lets us do things like only starting the drag when the
15099      * header element within the linked html element is clicked.
15100      * @property handleElId
15101      * @type String
15102      * @private
15103      */
15104     handleElId: null,
15105
15106     /**
15107      * An associative array of HTML tags that will be ignored if clicked.
15108      * @property invalidHandleTypes
15109      * @type {string: string}
15110      */
15111     invalidHandleTypes: null,
15112
15113     /**
15114      * An associative array of ids for elements that will be ignored if clicked
15115      * @property invalidHandleIds
15116      * @type {string: string}
15117      */
15118     invalidHandleIds: null,
15119
15120     /**
15121      * An indexted array of css class names for elements that will be ignored
15122      * if clicked.
15123      * @property invalidHandleClasses
15124      * @type string[]
15125      */
15126     invalidHandleClasses: null,
15127
15128     /**
15129      * The linked element's absolute X position at the time the drag was
15130      * started
15131      * @property startPageX
15132      * @type int
15133      * @private
15134      */
15135     startPageX: 0,
15136
15137     /**
15138      * The linked element's absolute X position at the time the drag was
15139      * started
15140      * @property startPageY
15141      * @type int
15142      * @private
15143      */
15144     startPageY: 0,
15145
15146     /**
15147      * The group defines a logical collection of DragDrop objects that are
15148      * related.  Instances only get events when interacting with other
15149      * DragDrop object in the same group.  This lets us define multiple
15150      * groups using a single DragDrop subclass if we want.
15151      * @property groups
15152      * @type {string: string}
15153      */
15154     groups: null,
15155
15156     /**
15157      * Individual drag/drop instances can be locked.  This will prevent
15158      * onmousedown start drag.
15159      * @property locked
15160      * @type boolean
15161      * @private
15162      */
15163     locked: false,
15164
15165     /**
15166      * Lock this instance
15167      * @method lock
15168      */
15169     lock: function() { this.locked = true; },
15170
15171     /**
15172      * Unlock this instace
15173      * @method unlock
15174      */
15175     unlock: function() { this.locked = false; },
15176
15177     /**
15178      * By default, all insances can be a drop target.  This can be disabled by
15179      * setting isTarget to false.
15180      * @method isTarget
15181      * @type boolean
15182      */
15183     isTarget: true,
15184
15185     /**
15186      * The padding configured for this drag and drop object for calculating
15187      * the drop zone intersection with this object.
15188      * @method padding
15189      * @type int[]
15190      */
15191     padding: null,
15192
15193     /**
15194      * Cached reference to the linked element
15195      * @property _domRef
15196      * @private
15197      */
15198     _domRef: null,
15199
15200     /**
15201      * Internal typeof flag
15202      * @property __ygDragDrop
15203      * @private
15204      */
15205     __ygDragDrop: true,
15206
15207     /**
15208      * Set to true when horizontal contraints are applied
15209      * @property constrainX
15210      * @type boolean
15211      * @private
15212      */
15213     constrainX: false,
15214
15215     /**
15216      * Set to true when vertical contraints are applied
15217      * @property constrainY
15218      * @type boolean
15219      * @private
15220      */
15221     constrainY: false,
15222
15223     /**
15224      * The left constraint
15225      * @property minX
15226      * @type int
15227      * @private
15228      */
15229     minX: 0,
15230
15231     /**
15232      * The right constraint
15233      * @property maxX
15234      * @type int
15235      * @private
15236      */
15237     maxX: 0,
15238
15239     /**
15240      * The up constraint
15241      * @property minY
15242      * @type int
15243      * @type int
15244      * @private
15245      */
15246     minY: 0,
15247
15248     /**
15249      * The down constraint
15250      * @property maxY
15251      * @type int
15252      * @private
15253      */
15254     maxY: 0,
15255
15256     /**
15257      * Maintain offsets when we resetconstraints.  Set to true when you want
15258      * the position of the element relative to its parent to stay the same
15259      * when the page changes
15260      *
15261      * @property maintainOffset
15262      * @type boolean
15263      */
15264     maintainOffset: false,
15265
15266     /**
15267      * Array of pixel locations the element will snap to if we specified a
15268      * horizontal graduation/interval.  This array is generated automatically
15269      * when you define a tick interval.
15270      * @property xTicks
15271      * @type int[]
15272      */
15273     xTicks: null,
15274
15275     /**
15276      * Array of pixel locations the element will snap to if we specified a
15277      * vertical graduation/interval.  This array is generated automatically
15278      * when you define a tick interval.
15279      * @property yTicks
15280      * @type int[]
15281      */
15282     yTicks: null,
15283
15284     /**
15285      * By default the drag and drop instance will only respond to the primary
15286      * button click (left button for a right-handed mouse).  Set to true to
15287      * allow drag and drop to start with any mouse click that is propogated
15288      * by the browser
15289      * @property primaryButtonOnly
15290      * @type boolean
15291      */
15292     primaryButtonOnly: true,
15293
15294     /**
15295      * The availabe property is false until the linked dom element is accessible.
15296      * @property available
15297      * @type boolean
15298      */
15299     available: false,
15300
15301     /**
15302      * By default, drags can only be initiated if the mousedown occurs in the
15303      * region the linked element is.  This is done in part to work around a
15304      * bug in some browsers that mis-report the mousedown if the previous
15305      * mouseup happened outside of the window.  This property is set to true
15306      * if outer handles are defined.
15307      *
15308      * @property hasOuterHandles
15309      * @type boolean
15310      * @default false
15311      */
15312     hasOuterHandles: false,
15313
15314     /**
15315      * Code that executes immediately before the startDrag event
15316      * @method b4StartDrag
15317      * @private
15318      */
15319     b4StartDrag: function(x, y) { },
15320
15321     /**
15322      * Abstract method called after a drag/drop object is clicked
15323      * and the drag or mousedown time thresholds have beeen met.
15324      * @method startDrag
15325      * @param {int} X click location
15326      * @param {int} Y click location
15327      */
15328     startDrag: function(x, y) { /* override this */ },
15329
15330     /**
15331      * Code that executes immediately before the onDrag event
15332      * @method b4Drag
15333      * @private
15334      */
15335     b4Drag: function(e) { },
15336
15337     /**
15338      * Abstract method called during the onMouseMove event while dragging an
15339      * object.
15340      * @method onDrag
15341      * @param {Event} e the mousemove event
15342      */
15343     onDrag: function(e) { /* override this */ },
15344
15345     /**
15346      * Abstract method called when this element fist begins hovering over
15347      * another DragDrop obj
15348      * @method onDragEnter
15349      * @param {Event} e the mousemove event
15350      * @param {String|DragDrop[]} id In POINT mode, the element
15351      * id this is hovering over.  In INTERSECT mode, an array of one or more
15352      * dragdrop items being hovered over.
15353      */
15354     onDragEnter: function(e, id) { /* override this */ },
15355
15356     /**
15357      * Code that executes immediately before the onDragOver event
15358      * @method b4DragOver
15359      * @private
15360      */
15361     b4DragOver: function(e) { },
15362
15363     /**
15364      * Abstract method called when this element is hovering over another
15365      * DragDrop obj
15366      * @method onDragOver
15367      * @param {Event} e the mousemove event
15368      * @param {String|DragDrop[]} id In POINT mode, the element
15369      * id this is hovering over.  In INTERSECT mode, an array of dd items
15370      * being hovered over.
15371      */
15372     onDragOver: function(e, id) { /* override this */ },
15373
15374     /**
15375      * Code that executes immediately before the onDragOut event
15376      * @method b4DragOut
15377      * @private
15378      */
15379     b4DragOut: function(e) { },
15380
15381     /**
15382      * Abstract method called when we are no longer hovering over an element
15383      * @method onDragOut
15384      * @param {Event} e the mousemove event
15385      * @param {String|DragDrop[]} id In POINT mode, the element
15386      * id this was hovering over.  In INTERSECT mode, an array of dd items
15387      * that the mouse is no longer over.
15388      */
15389     onDragOut: function(e, id) { /* override this */ },
15390
15391     /**
15392      * Code that executes immediately before the onDragDrop event
15393      * @method b4DragDrop
15394      * @private
15395      */
15396     b4DragDrop: function(e) { },
15397
15398     /**
15399      * Abstract method called when this item is dropped on another DragDrop
15400      * obj
15401      * @method onDragDrop
15402      * @param {Event} e the mouseup event
15403      * @param {String|DragDrop[]} id In POINT mode, the element
15404      * id this was dropped on.  In INTERSECT mode, an array of dd items this
15405      * was dropped on.
15406      */
15407     onDragDrop: function(e, id) { /* override this */ },
15408
15409     /**
15410      * Abstract method called when this item is dropped on an area with no
15411      * drop target
15412      * @method onInvalidDrop
15413      * @param {Event} e the mouseup event
15414      */
15415     onInvalidDrop: function(e) { /* override this */ },
15416
15417     /**
15418      * Code that executes immediately before the endDrag event
15419      * @method b4EndDrag
15420      * @private
15421      */
15422     b4EndDrag: function(e) { },
15423
15424     /**
15425      * Fired when we are done dragging the object
15426      * @method endDrag
15427      * @param {Event} e the mouseup event
15428      */
15429     endDrag: function(e) { /* override this */ },
15430
15431     /**
15432      * Code executed immediately before the onMouseDown event
15433      * @method b4MouseDown
15434      * @param {Event} e the mousedown event
15435      * @private
15436      */
15437     b4MouseDown: function(e) {  },
15438
15439     /**
15440      * Event handler that fires when a drag/drop obj gets a mousedown
15441      * @method onMouseDown
15442      * @param {Event} e the mousedown event
15443      */
15444     onMouseDown: function(e) { /* override this */ },
15445
15446     /**
15447      * Event handler that fires when a drag/drop obj gets a mouseup
15448      * @method onMouseUp
15449      * @param {Event} e the mouseup event
15450      */
15451     onMouseUp: function(e) { /* override this */ },
15452
15453     /**
15454      * Override the onAvailable method to do what is needed after the initial
15455      * position was determined.
15456      * @method onAvailable
15457      */
15458     onAvailable: function () {
15459     },
15460
15461     /*
15462      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
15463      * @type Object
15464      */
15465     defaultPadding : {left:0, right:0, top:0, bottom:0},
15466
15467     /*
15468      * Initializes the drag drop object's constraints to restrict movement to a certain element.
15469  *
15470  * Usage:
15471  <pre><code>
15472  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
15473                 { dragElId: "existingProxyDiv" });
15474  dd.startDrag = function(){
15475      this.constrainTo("parent-id");
15476  };
15477  </code></pre>
15478  * Or you can initalize it using the {@link Roo.Element} object:
15479  <pre><code>
15480  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
15481      startDrag : function(){
15482          this.constrainTo("parent-id");
15483      }
15484  });
15485  </code></pre>
15486      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
15487      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
15488      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
15489      * an object containing the sides to pad. For example: {right:10, bottom:10}
15490      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
15491      */
15492     constrainTo : function(constrainTo, pad, inContent){
15493         if(typeof pad == "number"){
15494             pad = {left: pad, right:pad, top:pad, bottom:pad};
15495         }
15496         pad = pad || this.defaultPadding;
15497         var b = Roo.get(this.getEl()).getBox();
15498         var ce = Roo.get(constrainTo);
15499         var s = ce.getScroll();
15500         var c, cd = ce.dom;
15501         if(cd == document.body){
15502             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15503         }else{
15504             xy = ce.getXY();
15505             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15506         }
15507
15508
15509         var topSpace = b.y - c.y;
15510         var leftSpace = b.x - c.x;
15511
15512         this.resetConstraints();
15513         this.setXConstraint(leftSpace - (pad.left||0), // left
15514                 c.width - leftSpace - b.width - (pad.right||0) //right
15515         );
15516         this.setYConstraint(topSpace - (pad.top||0), //top
15517                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15518         );
15519     },
15520
15521     /**
15522      * Returns a reference to the linked element
15523      * @method getEl
15524      * @return {HTMLElement} the html element
15525      */
15526     getEl: function() {
15527         if (!this._domRef) {
15528             this._domRef = Roo.getDom(this.id);
15529         }
15530
15531         return this._domRef;
15532     },
15533
15534     /**
15535      * Returns a reference to the actual element to drag.  By default this is
15536      * the same as the html element, but it can be assigned to another
15537      * element. An example of this can be found in Roo.dd.DDProxy
15538      * @method getDragEl
15539      * @return {HTMLElement} the html element
15540      */
15541     getDragEl: function() {
15542         return Roo.getDom(this.dragElId);
15543     },
15544
15545     /**
15546      * Sets up the DragDrop object.  Must be called in the constructor of any
15547      * Roo.dd.DragDrop subclass
15548      * @method init
15549      * @param id the id of the linked element
15550      * @param {String} sGroup the group of related items
15551      * @param {object} config configuration attributes
15552      */
15553     init: function(id, sGroup, config) {
15554         this.initTarget(id, sGroup, config);
15555         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15556         // Event.on(this.id, "selectstart", Event.preventDefault);
15557     },
15558
15559     /**
15560      * Initializes Targeting functionality only... the object does not
15561      * get a mousedown handler.
15562      * @method initTarget
15563      * @param id the id of the linked element
15564      * @param {String} sGroup the group of related items
15565      * @param {object} config configuration attributes
15566      */
15567     initTarget: function(id, sGroup, config) {
15568
15569         // configuration attributes
15570         this.config = config || {};
15571
15572         // create a local reference to the drag and drop manager
15573         this.DDM = Roo.dd.DDM;
15574         // initialize the groups array
15575         this.groups = {};
15576
15577         // assume that we have an element reference instead of an id if the
15578         // parameter is not a string
15579         if (typeof id !== "string") {
15580             id = Roo.id(id);
15581         }
15582
15583         // set the id
15584         this.id = id;
15585
15586         // add to an interaction group
15587         this.addToGroup((sGroup) ? sGroup : "default");
15588
15589         // We don't want to register this as the handle with the manager
15590         // so we just set the id rather than calling the setter.
15591         this.handleElId = id;
15592
15593         // the linked element is the element that gets dragged by default
15594         this.setDragElId(id);
15595
15596         // by default, clicked anchors will not start drag operations.
15597         this.invalidHandleTypes = { A: "A" };
15598         this.invalidHandleIds = {};
15599         this.invalidHandleClasses = [];
15600
15601         this.applyConfig();
15602
15603         this.handleOnAvailable();
15604     },
15605
15606     /**
15607      * Applies the configuration parameters that were passed into the constructor.
15608      * This is supposed to happen at each level through the inheritance chain.  So
15609      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15610      * DragDrop in order to get all of the parameters that are available in
15611      * each object.
15612      * @method applyConfig
15613      */
15614     applyConfig: function() {
15615
15616         // configurable properties:
15617         //    padding, isTarget, maintainOffset, primaryButtonOnly
15618         this.padding           = this.config.padding || [0, 0, 0, 0];
15619         this.isTarget          = (this.config.isTarget !== false);
15620         this.maintainOffset    = (this.config.maintainOffset);
15621         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15622
15623     },
15624
15625     /**
15626      * Executed when the linked element is available
15627      * @method handleOnAvailable
15628      * @private
15629      */
15630     handleOnAvailable: function() {
15631         this.available = true;
15632         this.resetConstraints();
15633         this.onAvailable();
15634     },
15635
15636      /**
15637      * Configures the padding for the target zone in px.  Effectively expands
15638      * (or reduces) the virtual object size for targeting calculations.
15639      * Supports css-style shorthand; if only one parameter is passed, all sides
15640      * will have that padding, and if only two are passed, the top and bottom
15641      * will have the first param, the left and right the second.
15642      * @method setPadding
15643      * @param {int} iTop    Top pad
15644      * @param {int} iRight  Right pad
15645      * @param {int} iBot    Bot pad
15646      * @param {int} iLeft   Left pad
15647      */
15648     setPadding: function(iTop, iRight, iBot, iLeft) {
15649         // this.padding = [iLeft, iRight, iTop, iBot];
15650         if (!iRight && 0 !== iRight) {
15651             this.padding = [iTop, iTop, iTop, iTop];
15652         } else if (!iBot && 0 !== iBot) {
15653             this.padding = [iTop, iRight, iTop, iRight];
15654         } else {
15655             this.padding = [iTop, iRight, iBot, iLeft];
15656         }
15657     },
15658
15659     /**
15660      * Stores the initial placement of the linked element.
15661      * @method setInitialPosition
15662      * @param {int} diffX   the X offset, default 0
15663      * @param {int} diffY   the Y offset, default 0
15664      */
15665     setInitPosition: function(diffX, diffY) {
15666         var el = this.getEl();
15667
15668         if (!this.DDM.verifyEl(el)) {
15669             return;
15670         }
15671
15672         var dx = diffX || 0;
15673         var dy = diffY || 0;
15674
15675         var p = Dom.getXY( el );
15676
15677         this.initPageX = p[0] - dx;
15678         this.initPageY = p[1] - dy;
15679
15680         this.lastPageX = p[0];
15681         this.lastPageY = p[1];
15682
15683
15684         this.setStartPosition(p);
15685     },
15686
15687     /**
15688      * Sets the start position of the element.  This is set when the obj
15689      * is initialized, the reset when a drag is started.
15690      * @method setStartPosition
15691      * @param pos current position (from previous lookup)
15692      * @private
15693      */
15694     setStartPosition: function(pos) {
15695         var p = pos || Dom.getXY( this.getEl() );
15696         this.deltaSetXY = null;
15697
15698         this.startPageX = p[0];
15699         this.startPageY = p[1];
15700     },
15701
15702     /**
15703      * Add this instance to a group of related drag/drop objects.  All
15704      * instances belong to at least one group, and can belong to as many
15705      * groups as needed.
15706      * @method addToGroup
15707      * @param sGroup {string} the name of the group
15708      */
15709     addToGroup: function(sGroup) {
15710         this.groups[sGroup] = true;
15711         this.DDM.regDragDrop(this, sGroup);
15712     },
15713
15714     /**
15715      * Remove's this instance from the supplied interaction group
15716      * @method removeFromGroup
15717      * @param {string}  sGroup  The group to drop
15718      */
15719     removeFromGroup: function(sGroup) {
15720         if (this.groups[sGroup]) {
15721             delete this.groups[sGroup];
15722         }
15723
15724         this.DDM.removeDDFromGroup(this, sGroup);
15725     },
15726
15727     /**
15728      * Allows you to specify that an element other than the linked element
15729      * will be moved with the cursor during a drag
15730      * @method setDragElId
15731      * @param id {string} the id of the element that will be used to initiate the drag
15732      */
15733     setDragElId: function(id) {
15734         this.dragElId = id;
15735     },
15736
15737     /**
15738      * Allows you to specify a child of the linked element that should be
15739      * used to initiate the drag operation.  An example of this would be if
15740      * you have a content div with text and links.  Clicking anywhere in the
15741      * content area would normally start the drag operation.  Use this method
15742      * to specify that an element inside of the content div is the element
15743      * that starts the drag operation.
15744      * @method setHandleElId
15745      * @param id {string} the id of the element that will be used to
15746      * initiate the drag.
15747      */
15748     setHandleElId: function(id) {
15749         if (typeof id !== "string") {
15750             id = Roo.id(id);
15751         }
15752         this.handleElId = id;
15753         this.DDM.regHandle(this.id, id);
15754     },
15755
15756     /**
15757      * Allows you to set an element outside of the linked element as a drag
15758      * handle
15759      * @method setOuterHandleElId
15760      * @param id the id of the element that will be used to initiate the drag
15761      */
15762     setOuterHandleElId: function(id) {
15763         if (typeof id !== "string") {
15764             id = Roo.id(id);
15765         }
15766         Event.on(id, "mousedown",
15767                 this.handleMouseDown, this);
15768         this.setHandleElId(id);
15769
15770         this.hasOuterHandles = true;
15771     },
15772
15773     /**
15774      * Remove all drag and drop hooks for this element
15775      * @method unreg
15776      */
15777     unreg: function() {
15778         Event.un(this.id, "mousedown",
15779                 this.handleMouseDown);
15780         this._domRef = null;
15781         this.DDM._remove(this);
15782     },
15783
15784     destroy : function(){
15785         this.unreg();
15786     },
15787
15788     /**
15789      * Returns true if this instance is locked, or the drag drop mgr is locked
15790      * (meaning that all drag/drop is disabled on the page.)
15791      * @method isLocked
15792      * @return {boolean} true if this obj or all drag/drop is locked, else
15793      * false
15794      */
15795     isLocked: function() {
15796         return (this.DDM.isLocked() || this.locked);
15797     },
15798
15799     /**
15800      * Fired when this object is clicked
15801      * @method handleMouseDown
15802      * @param {Event} e
15803      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15804      * @private
15805      */
15806     handleMouseDown: function(e, oDD){
15807         if (this.primaryButtonOnly && e.button != 0) {
15808             return;
15809         }
15810
15811         if (this.isLocked()) {
15812             return;
15813         }
15814
15815         this.DDM.refreshCache(this.groups);
15816
15817         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15818         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15819         } else {
15820             if (this.clickValidator(e)) {
15821
15822                 // set the initial element position
15823                 this.setStartPosition();
15824
15825
15826                 this.b4MouseDown(e);
15827                 this.onMouseDown(e);
15828
15829                 this.DDM.handleMouseDown(e, this);
15830
15831                 this.DDM.stopEvent(e);
15832             } else {
15833
15834
15835             }
15836         }
15837     },
15838
15839     clickValidator: function(e) {
15840         var target = e.getTarget();
15841         return ( this.isValidHandleChild(target) &&
15842                     (this.id == this.handleElId ||
15843                         this.DDM.handleWasClicked(target, this.id)) );
15844     },
15845
15846     /**
15847      * Allows you to specify a tag name that should not start a drag operation
15848      * when clicked.  This is designed to facilitate embedding links within a
15849      * drag handle that do something other than start the drag.
15850      * @method addInvalidHandleType
15851      * @param {string} tagName the type of element to exclude
15852      */
15853     addInvalidHandleType: function(tagName) {
15854         var type = tagName.toUpperCase();
15855         this.invalidHandleTypes[type] = type;
15856     },
15857
15858     /**
15859      * Lets you to specify an element id for a child of a drag handle
15860      * that should not initiate a drag
15861      * @method addInvalidHandleId
15862      * @param {string} id the element id of the element you wish to ignore
15863      */
15864     addInvalidHandleId: function(id) {
15865         if (typeof id !== "string") {
15866             id = Roo.id(id);
15867         }
15868         this.invalidHandleIds[id] = id;
15869     },
15870
15871     /**
15872      * Lets you specify a css class of elements that will not initiate a drag
15873      * @method addInvalidHandleClass
15874      * @param {string} cssClass the class of the elements you wish to ignore
15875      */
15876     addInvalidHandleClass: function(cssClass) {
15877         this.invalidHandleClasses.push(cssClass);
15878     },
15879
15880     /**
15881      * Unsets an excluded tag name set by addInvalidHandleType
15882      * @method removeInvalidHandleType
15883      * @param {string} tagName the type of element to unexclude
15884      */
15885     removeInvalidHandleType: function(tagName) {
15886         var type = tagName.toUpperCase();
15887         // this.invalidHandleTypes[type] = null;
15888         delete this.invalidHandleTypes[type];
15889     },
15890
15891     /**
15892      * Unsets an invalid handle id
15893      * @method removeInvalidHandleId
15894      * @param {string} id the id of the element to re-enable
15895      */
15896     removeInvalidHandleId: function(id) {
15897         if (typeof id !== "string") {
15898             id = Roo.id(id);
15899         }
15900         delete this.invalidHandleIds[id];
15901     },
15902
15903     /**
15904      * Unsets an invalid css class
15905      * @method removeInvalidHandleClass
15906      * @param {string} cssClass the class of the element(s) you wish to
15907      * re-enable
15908      */
15909     removeInvalidHandleClass: function(cssClass) {
15910         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15911             if (this.invalidHandleClasses[i] == cssClass) {
15912                 delete this.invalidHandleClasses[i];
15913             }
15914         }
15915     },
15916
15917     /**
15918      * Checks the tag exclusion list to see if this click should be ignored
15919      * @method isValidHandleChild
15920      * @param {HTMLElement} node the HTMLElement to evaluate
15921      * @return {boolean} true if this is a valid tag type, false if not
15922      */
15923     isValidHandleChild: function(node) {
15924
15925         var valid = true;
15926         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15927         var nodeName;
15928         try {
15929             nodeName = node.nodeName.toUpperCase();
15930         } catch(e) {
15931             nodeName = node.nodeName;
15932         }
15933         valid = valid && !this.invalidHandleTypes[nodeName];
15934         valid = valid && !this.invalidHandleIds[node.id];
15935
15936         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15937             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15938         }
15939
15940
15941         return valid;
15942
15943     },
15944
15945     /**
15946      * Create the array of horizontal tick marks if an interval was specified
15947      * in setXConstraint().
15948      * @method setXTicks
15949      * @private
15950      */
15951     setXTicks: function(iStartX, iTickSize) {
15952         this.xTicks = [];
15953         this.xTickSize = iTickSize;
15954
15955         var tickMap = {};
15956
15957         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15958             if (!tickMap[i]) {
15959                 this.xTicks[this.xTicks.length] = i;
15960                 tickMap[i] = true;
15961             }
15962         }
15963
15964         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15965             if (!tickMap[i]) {
15966                 this.xTicks[this.xTicks.length] = i;
15967                 tickMap[i] = true;
15968             }
15969         }
15970
15971         this.xTicks.sort(this.DDM.numericSort) ;
15972     },
15973
15974     /**
15975      * Create the array of vertical tick marks if an interval was specified in
15976      * setYConstraint().
15977      * @method setYTicks
15978      * @private
15979      */
15980     setYTicks: function(iStartY, iTickSize) {
15981         this.yTicks = [];
15982         this.yTickSize = iTickSize;
15983
15984         var tickMap = {};
15985
15986         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15987             if (!tickMap[i]) {
15988                 this.yTicks[this.yTicks.length] = i;
15989                 tickMap[i] = true;
15990             }
15991         }
15992
15993         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15994             if (!tickMap[i]) {
15995                 this.yTicks[this.yTicks.length] = i;
15996                 tickMap[i] = true;
15997             }
15998         }
15999
16000         this.yTicks.sort(this.DDM.numericSort) ;
16001     },
16002
16003     /**
16004      * By default, the element can be dragged any place on the screen.  Use
16005      * this method to limit the horizontal travel of the element.  Pass in
16006      * 0,0 for the parameters if you want to lock the drag to the y axis.
16007      * @method setXConstraint
16008      * @param {int} iLeft the number of pixels the element can move to the left
16009      * @param {int} iRight the number of pixels the element can move to the
16010      * right
16011      * @param {int} iTickSize optional parameter for specifying that the
16012      * element
16013      * should move iTickSize pixels at a time.
16014      */
16015     setXConstraint: function(iLeft, iRight, iTickSize) {
16016         this.leftConstraint = iLeft;
16017         this.rightConstraint = iRight;
16018
16019         this.minX = this.initPageX - iLeft;
16020         this.maxX = this.initPageX + iRight;
16021         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
16022
16023         this.constrainX = true;
16024     },
16025
16026     /**
16027      * Clears any constraints applied to this instance.  Also clears ticks
16028      * since they can't exist independent of a constraint at this time.
16029      * @method clearConstraints
16030      */
16031     clearConstraints: function() {
16032         this.constrainX = false;
16033         this.constrainY = false;
16034         this.clearTicks();
16035     },
16036
16037     /**
16038      * Clears any tick interval defined for this instance
16039      * @method clearTicks
16040      */
16041     clearTicks: function() {
16042         this.xTicks = null;
16043         this.yTicks = null;
16044         this.xTickSize = 0;
16045         this.yTickSize = 0;
16046     },
16047
16048     /**
16049      * By default, the element can be dragged any place on the screen.  Set
16050      * this to limit the vertical travel of the element.  Pass in 0,0 for the
16051      * parameters if you want to lock the drag to the x axis.
16052      * @method setYConstraint
16053      * @param {int} iUp the number of pixels the element can move up
16054      * @param {int} iDown the number of pixels the element can move down
16055      * @param {int} iTickSize optional parameter for specifying that the
16056      * element should move iTickSize pixels at a time.
16057      */
16058     setYConstraint: function(iUp, iDown, iTickSize) {
16059         this.topConstraint = iUp;
16060         this.bottomConstraint = iDown;
16061
16062         this.minY = this.initPageY - iUp;
16063         this.maxY = this.initPageY + iDown;
16064         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
16065
16066         this.constrainY = true;
16067
16068     },
16069
16070     /**
16071      * resetConstraints must be called if you manually reposition a dd element.
16072      * @method resetConstraints
16073      * @param {boolean} maintainOffset
16074      */
16075     resetConstraints: function() {
16076
16077
16078         // Maintain offsets if necessary
16079         if (this.initPageX || this.initPageX === 0) {
16080             // figure out how much this thing has moved
16081             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
16082             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
16083
16084             this.setInitPosition(dx, dy);
16085
16086         // This is the first time we have detected the element's position
16087         } else {
16088             this.setInitPosition();
16089         }
16090
16091         if (this.constrainX) {
16092             this.setXConstraint( this.leftConstraint,
16093                                  this.rightConstraint,
16094                                  this.xTickSize        );
16095         }
16096
16097         if (this.constrainY) {
16098             this.setYConstraint( this.topConstraint,
16099                                  this.bottomConstraint,
16100                                  this.yTickSize         );
16101         }
16102     },
16103
16104     /**
16105      * Normally the drag element is moved pixel by pixel, but we can specify
16106      * that it move a number of pixels at a time.  This method resolves the
16107      * location when we have it set up like this.
16108      * @method getTick
16109      * @param {int} val where we want to place the object
16110      * @param {int[]} tickArray sorted array of valid points
16111      * @return {int} the closest tick
16112      * @private
16113      */
16114     getTick: function(val, tickArray) {
16115
16116         if (!tickArray) {
16117             // If tick interval is not defined, it is effectively 1 pixel,
16118             // so we return the value passed to us.
16119             return val;
16120         } else if (tickArray[0] >= val) {
16121             // The value is lower than the first tick, so we return the first
16122             // tick.
16123             return tickArray[0];
16124         } else {
16125             for (var i=0, len=tickArray.length; i<len; ++i) {
16126                 var next = i + 1;
16127                 if (tickArray[next] && tickArray[next] >= val) {
16128                     var diff1 = val - tickArray[i];
16129                     var diff2 = tickArray[next] - val;
16130                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
16131                 }
16132             }
16133
16134             // The value is larger than the last tick, so we return the last
16135             // tick.
16136             return tickArray[tickArray.length - 1];
16137         }
16138     },
16139
16140     /**
16141      * toString method
16142      * @method toString
16143      * @return {string} string representation of the dd obj
16144      */
16145     toString: function() {
16146         return ("DragDrop " + this.id);
16147     }
16148
16149 });
16150
16151 })();
16152 /*
16153  * Based on:
16154  * Ext JS Library 1.1.1
16155  * Copyright(c) 2006-2007, Ext JS, LLC.
16156  *
16157  * Originally Released Under LGPL - original licence link has changed is not relivant.
16158  *
16159  * Fork - LGPL
16160  * <script type="text/javascript">
16161  */
16162
16163
16164 /**
16165  * The drag and drop utility provides a framework for building drag and drop
16166  * applications.  In addition to enabling drag and drop for specific elements,
16167  * the drag and drop elements are tracked by the manager class, and the
16168  * interactions between the various elements are tracked during the drag and
16169  * the implementing code is notified about these important moments.
16170  */
16171
16172 // Only load the library once.  Rewriting the manager class would orphan
16173 // existing drag and drop instances.
16174 if (!Roo.dd.DragDropMgr) {
16175
16176 /**
16177  * @class Roo.dd.DragDropMgr
16178  * DragDropMgr is a singleton that tracks the element interaction for
16179  * all DragDrop items in the window.  Generally, you will not call
16180  * this class directly, but it does have helper methods that could
16181  * be useful in your DragDrop implementations.
16182  * @singleton
16183  */
16184 Roo.dd.DragDropMgr = function() {
16185
16186     var Event = Roo.EventManager;
16187
16188     return {
16189
16190         /**
16191          * Two dimensional Array of registered DragDrop objects.  The first
16192          * dimension is the DragDrop item group, the second the DragDrop
16193          * object.
16194          * @property ids
16195          * @type {string: string}
16196          * @private
16197          * @static
16198          */
16199         ids: {},
16200
16201         /**
16202          * Array of element ids defined as drag handles.  Used to determine
16203          * if the element that generated the mousedown event is actually the
16204          * handle and not the html element itself.
16205          * @property handleIds
16206          * @type {string: string}
16207          * @private
16208          * @static
16209          */
16210         handleIds: {},
16211
16212         /**
16213          * the DragDrop object that is currently being dragged
16214          * @property dragCurrent
16215          * @type DragDrop
16216          * @private
16217          * @static
16218          **/
16219         dragCurrent: null,
16220
16221         /**
16222          * the DragDrop object(s) that are being hovered over
16223          * @property dragOvers
16224          * @type Array
16225          * @private
16226          * @static
16227          */
16228         dragOvers: {},
16229
16230         /**
16231          * the X distance between the cursor and the object being dragged
16232          * @property deltaX
16233          * @type int
16234          * @private
16235          * @static
16236          */
16237         deltaX: 0,
16238
16239         /**
16240          * the Y distance between the cursor and the object being dragged
16241          * @property deltaY
16242          * @type int
16243          * @private
16244          * @static
16245          */
16246         deltaY: 0,
16247
16248         /**
16249          * Flag to determine if we should prevent the default behavior of the
16250          * events we define. By default this is true, but this can be set to
16251          * false if you need the default behavior (not recommended)
16252          * @property preventDefault
16253          * @type boolean
16254          * @static
16255          */
16256         preventDefault: true,
16257
16258         /**
16259          * Flag to determine if we should stop the propagation of the events
16260          * we generate. This is true by default but you may want to set it to
16261          * false if the html element contains other features that require the
16262          * mouse click.
16263          * @property stopPropagation
16264          * @type boolean
16265          * @static
16266          */
16267         stopPropagation: true,
16268
16269         /**
16270          * Internal flag that is set to true when drag and drop has been
16271          * intialized
16272          * @property initialized
16273          * @private
16274          * @static
16275          */
16276         initalized: false,
16277
16278         /**
16279          * All drag and drop can be disabled.
16280          * @property locked
16281          * @private
16282          * @static
16283          */
16284         locked: false,
16285
16286         /**
16287          * Called the first time an element is registered.
16288          * @method init
16289          * @private
16290          * @static
16291          */
16292         init: function() {
16293             this.initialized = true;
16294         },
16295
16296         /**
16297          * In point mode, drag and drop interaction is defined by the
16298          * location of the cursor during the drag/drop
16299          * @property POINT
16300          * @type int
16301          * @static
16302          */
16303         POINT: 0,
16304
16305         /**
16306          * In intersect mode, drag and drop interactio nis defined by the
16307          * overlap of two or more drag and drop objects.
16308          * @property INTERSECT
16309          * @type int
16310          * @static
16311          */
16312         INTERSECT: 1,
16313
16314         /**
16315          * The current drag and drop mode.  Default: POINT
16316          * @property mode
16317          * @type int
16318          * @static
16319          */
16320         mode: 0,
16321
16322         /**
16323          * Runs method on all drag and drop objects
16324          * @method _execOnAll
16325          * @private
16326          * @static
16327          */
16328         _execOnAll: function(sMethod, args) {
16329             for (var i in this.ids) {
16330                 for (var j in this.ids[i]) {
16331                     var oDD = this.ids[i][j];
16332                     if (! this.isTypeOfDD(oDD)) {
16333                         continue;
16334                     }
16335                     oDD[sMethod].apply(oDD, args);
16336                 }
16337             }
16338         },
16339
16340         /**
16341          * Drag and drop initialization.  Sets up the global event handlers
16342          * @method _onLoad
16343          * @private
16344          * @static
16345          */
16346         _onLoad: function() {
16347
16348             this.init();
16349
16350
16351             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
16352             Event.on(document, "mousemove", this.handleMouseMove, this, true);
16353             Event.on(window,   "unload",    this._onUnload, this, true);
16354             Event.on(window,   "resize",    this._onResize, this, true);
16355             // Event.on(window,   "mouseout",    this._test);
16356
16357         },
16358
16359         /**
16360          * Reset constraints on all drag and drop objs
16361          * @method _onResize
16362          * @private
16363          * @static
16364          */
16365         _onResize: function(e) {
16366             this._execOnAll("resetConstraints", []);
16367         },
16368
16369         /**
16370          * Lock all drag and drop functionality
16371          * @method lock
16372          * @static
16373          */
16374         lock: function() { this.locked = true; },
16375
16376         /**
16377          * Unlock all drag and drop functionality
16378          * @method unlock
16379          * @static
16380          */
16381         unlock: function() { this.locked = false; },
16382
16383         /**
16384          * Is drag and drop locked?
16385          * @method isLocked
16386          * @return {boolean} True if drag and drop is locked, false otherwise.
16387          * @static
16388          */
16389         isLocked: function() { return this.locked; },
16390
16391         /**
16392          * Location cache that is set for all drag drop objects when a drag is
16393          * initiated, cleared when the drag is finished.
16394          * @property locationCache
16395          * @private
16396          * @static
16397          */
16398         locationCache: {},
16399
16400         /**
16401          * Set useCache to false if you want to force object the lookup of each
16402          * drag and drop linked element constantly during a drag.
16403          * @property useCache
16404          * @type boolean
16405          * @static
16406          */
16407         useCache: true,
16408
16409         /**
16410          * The number of pixels that the mouse needs to move after the
16411          * mousedown before the drag is initiated.  Default=3;
16412          * @property clickPixelThresh
16413          * @type int
16414          * @static
16415          */
16416         clickPixelThresh: 3,
16417
16418         /**
16419          * The number of milliseconds after the mousedown event to initiate the
16420          * drag if we don't get a mouseup event. Default=1000
16421          * @property clickTimeThresh
16422          * @type int
16423          * @static
16424          */
16425         clickTimeThresh: 350,
16426
16427         /**
16428          * Flag that indicates that either the drag pixel threshold or the
16429          * mousdown time threshold has been met
16430          * @property dragThreshMet
16431          * @type boolean
16432          * @private
16433          * @static
16434          */
16435         dragThreshMet: false,
16436
16437         /**
16438          * Timeout used for the click time threshold
16439          * @property clickTimeout
16440          * @type Object
16441          * @private
16442          * @static
16443          */
16444         clickTimeout: null,
16445
16446         /**
16447          * The X position of the mousedown event stored for later use when a
16448          * drag threshold is met.
16449          * @property startX
16450          * @type int
16451          * @private
16452          * @static
16453          */
16454         startX: 0,
16455
16456         /**
16457          * The Y position of the mousedown event stored for later use when a
16458          * drag threshold is met.
16459          * @property startY
16460          * @type int
16461          * @private
16462          * @static
16463          */
16464         startY: 0,
16465
16466         /**
16467          * Each DragDrop instance must be registered with the DragDropMgr.
16468          * This is executed in DragDrop.init()
16469          * @method regDragDrop
16470          * @param {DragDrop} oDD the DragDrop object to register
16471          * @param {String} sGroup the name of the group this element belongs to
16472          * @static
16473          */
16474         regDragDrop: function(oDD, sGroup) {
16475             if (!this.initialized) { this.init(); }
16476
16477             if (!this.ids[sGroup]) {
16478                 this.ids[sGroup] = {};
16479             }
16480             this.ids[sGroup][oDD.id] = oDD;
16481         },
16482
16483         /**
16484          * Removes the supplied dd instance from the supplied group. Executed
16485          * by DragDrop.removeFromGroup, so don't call this function directly.
16486          * @method removeDDFromGroup
16487          * @private
16488          * @static
16489          */
16490         removeDDFromGroup: function(oDD, sGroup) {
16491             if (!this.ids[sGroup]) {
16492                 this.ids[sGroup] = {};
16493             }
16494
16495             var obj = this.ids[sGroup];
16496             if (obj && obj[oDD.id]) {
16497                 delete obj[oDD.id];
16498             }
16499         },
16500
16501         /**
16502          * Unregisters a drag and drop item.  This is executed in
16503          * DragDrop.unreg, use that method instead of calling this directly.
16504          * @method _remove
16505          * @private
16506          * @static
16507          */
16508         _remove: function(oDD) {
16509             for (var g in oDD.groups) {
16510                 if (g && this.ids[g][oDD.id]) {
16511                     delete this.ids[g][oDD.id];
16512                 }
16513             }
16514             delete this.handleIds[oDD.id];
16515         },
16516
16517         /**
16518          * Each DragDrop handle element must be registered.  This is done
16519          * automatically when executing DragDrop.setHandleElId()
16520          * @method regHandle
16521          * @param {String} sDDId the DragDrop id this element is a handle for
16522          * @param {String} sHandleId the id of the element that is the drag
16523          * handle
16524          * @static
16525          */
16526         regHandle: function(sDDId, sHandleId) {
16527             if (!this.handleIds[sDDId]) {
16528                 this.handleIds[sDDId] = {};
16529             }
16530             this.handleIds[sDDId][sHandleId] = sHandleId;
16531         },
16532
16533         /**
16534          * Utility function to determine if a given element has been
16535          * registered as a drag drop item.
16536          * @method isDragDrop
16537          * @param {String} id the element id to check
16538          * @return {boolean} true if this element is a DragDrop item,
16539          * false otherwise
16540          * @static
16541          */
16542         isDragDrop: function(id) {
16543             return ( this.getDDById(id) ) ? true : false;
16544         },
16545
16546         /**
16547          * Returns the drag and drop instances that are in all groups the
16548          * passed in instance belongs to.
16549          * @method getRelated
16550          * @param {DragDrop} p_oDD the obj to get related data for
16551          * @param {boolean} bTargetsOnly if true, only return targetable objs
16552          * @return {DragDrop[]} the related instances
16553          * @static
16554          */
16555         getRelated: function(p_oDD, bTargetsOnly) {
16556             var oDDs = [];
16557             for (var i in p_oDD.groups) {
16558                 for (j in this.ids[i]) {
16559                     var dd = this.ids[i][j];
16560                     if (! this.isTypeOfDD(dd)) {
16561                         continue;
16562                     }
16563                     if (!bTargetsOnly || dd.isTarget) {
16564                         oDDs[oDDs.length] = dd;
16565                     }
16566                 }
16567             }
16568
16569             return oDDs;
16570         },
16571
16572         /**
16573          * Returns true if the specified dd target is a legal target for
16574          * the specifice drag obj
16575          * @method isLegalTarget
16576          * @param {DragDrop} the drag obj
16577          * @param {DragDrop} the target
16578          * @return {boolean} true if the target is a legal target for the
16579          * dd obj
16580          * @static
16581          */
16582         isLegalTarget: function (oDD, oTargetDD) {
16583             var targets = this.getRelated(oDD, true);
16584             for (var i=0, len=targets.length;i<len;++i) {
16585                 if (targets[i].id == oTargetDD.id) {
16586                     return true;
16587                 }
16588             }
16589
16590             return false;
16591         },
16592
16593         /**
16594          * My goal is to be able to transparently determine if an object is
16595          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16596          * returns "object", oDD.constructor.toString() always returns
16597          * "DragDrop" and not the name of the subclass.  So for now it just
16598          * evaluates a well-known variable in DragDrop.
16599          * @method isTypeOfDD
16600          * @param {Object} the object to evaluate
16601          * @return {boolean} true if typeof oDD = DragDrop
16602          * @static
16603          */
16604         isTypeOfDD: function (oDD) {
16605             return (oDD && oDD.__ygDragDrop);
16606         },
16607
16608         /**
16609          * Utility function to determine if a given element has been
16610          * registered as a drag drop handle for the given Drag Drop object.
16611          * @method isHandle
16612          * @param {String} id the element id to check
16613          * @return {boolean} true if this element is a DragDrop handle, false
16614          * otherwise
16615          * @static
16616          */
16617         isHandle: function(sDDId, sHandleId) {
16618             return ( this.handleIds[sDDId] &&
16619                             this.handleIds[sDDId][sHandleId] );
16620         },
16621
16622         /**
16623          * Returns the DragDrop instance for a given id
16624          * @method getDDById
16625          * @param {String} id the id of the DragDrop object
16626          * @return {DragDrop} the drag drop object, null if it is not found
16627          * @static
16628          */
16629         getDDById: function(id) {
16630             for (var i in this.ids) {
16631                 if (this.ids[i][id]) {
16632                     return this.ids[i][id];
16633                 }
16634             }
16635             return null;
16636         },
16637
16638         /**
16639          * Fired after a registered DragDrop object gets the mousedown event.
16640          * Sets up the events required to track the object being dragged
16641          * @method handleMouseDown
16642          * @param {Event} e the event
16643          * @param oDD the DragDrop object being dragged
16644          * @private
16645          * @static
16646          */
16647         handleMouseDown: function(e, oDD) {
16648             if(Roo.QuickTips){
16649                 Roo.QuickTips.disable();
16650             }
16651             this.currentTarget = e.getTarget();
16652
16653             this.dragCurrent = oDD;
16654
16655             var el = oDD.getEl();
16656
16657             // track start position
16658             this.startX = e.getPageX();
16659             this.startY = e.getPageY();
16660
16661             this.deltaX = this.startX - el.offsetLeft;
16662             this.deltaY = this.startY - el.offsetTop;
16663
16664             this.dragThreshMet = false;
16665
16666             this.clickTimeout = setTimeout(
16667                     function() {
16668                         var DDM = Roo.dd.DDM;
16669                         DDM.startDrag(DDM.startX, DDM.startY);
16670                     },
16671                     this.clickTimeThresh );
16672         },
16673
16674         /**
16675          * Fired when either the drag pixel threshol or the mousedown hold
16676          * time threshold has been met.
16677          * @method startDrag
16678          * @param x {int} the X position of the original mousedown
16679          * @param y {int} the Y position of the original mousedown
16680          * @static
16681          */
16682         startDrag: function(x, y) {
16683             clearTimeout(this.clickTimeout);
16684             if (this.dragCurrent) {
16685                 this.dragCurrent.b4StartDrag(x, y);
16686                 this.dragCurrent.startDrag(x, y);
16687             }
16688             this.dragThreshMet = true;
16689         },
16690
16691         /**
16692          * Internal function to handle the mouseup event.  Will be invoked
16693          * from the context of the document.
16694          * @method handleMouseUp
16695          * @param {Event} e the event
16696          * @private
16697          * @static
16698          */
16699         handleMouseUp: function(e) {
16700
16701             if(Roo.QuickTips){
16702                 Roo.QuickTips.enable();
16703             }
16704             if (! this.dragCurrent) {
16705                 return;
16706             }
16707
16708             clearTimeout(this.clickTimeout);
16709
16710             if (this.dragThreshMet) {
16711                 this.fireEvents(e, true);
16712             } else {
16713             }
16714
16715             this.stopDrag(e);
16716
16717             this.stopEvent(e);
16718         },
16719
16720         /**
16721          * Utility to stop event propagation and event default, if these
16722          * features are turned on.
16723          * @method stopEvent
16724          * @param {Event} e the event as returned by this.getEvent()
16725          * @static
16726          */
16727         stopEvent: function(e){
16728             if(this.stopPropagation) {
16729                 e.stopPropagation();
16730             }
16731
16732             if (this.preventDefault) {
16733                 e.preventDefault();
16734             }
16735         },
16736
16737         /**
16738          * Internal function to clean up event handlers after the drag
16739          * operation is complete
16740          * @method stopDrag
16741          * @param {Event} e the event
16742          * @private
16743          * @static
16744          */
16745         stopDrag: function(e) {
16746             // Fire the drag end event for the item that was dragged
16747             if (this.dragCurrent) {
16748                 if (this.dragThreshMet) {
16749                     this.dragCurrent.b4EndDrag(e);
16750                     this.dragCurrent.endDrag(e);
16751                 }
16752
16753                 this.dragCurrent.onMouseUp(e);
16754             }
16755
16756             this.dragCurrent = null;
16757             this.dragOvers = {};
16758         },
16759
16760         /**
16761          * Internal function to handle the mousemove event.  Will be invoked
16762          * from the context of the html element.
16763          *
16764          * @TODO figure out what we can do about mouse events lost when the
16765          * user drags objects beyond the window boundary.  Currently we can
16766          * detect this in internet explorer by verifying that the mouse is
16767          * down during the mousemove event.  Firefox doesn't give us the
16768          * button state on the mousemove event.
16769          * @method handleMouseMove
16770          * @param {Event} e the event
16771          * @private
16772          * @static
16773          */
16774         handleMouseMove: function(e) {
16775             if (! this.dragCurrent) {
16776                 return true;
16777             }
16778
16779             // var button = e.which || e.button;
16780
16781             // check for IE mouseup outside of page boundary
16782             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16783                 this.stopEvent(e);
16784                 return this.handleMouseUp(e);
16785             }
16786
16787             if (!this.dragThreshMet) {
16788                 var diffX = Math.abs(this.startX - e.getPageX());
16789                 var diffY = Math.abs(this.startY - e.getPageY());
16790                 if (diffX > this.clickPixelThresh ||
16791                             diffY > this.clickPixelThresh) {
16792                     this.startDrag(this.startX, this.startY);
16793                 }
16794             }
16795
16796             if (this.dragThreshMet) {
16797                 this.dragCurrent.b4Drag(e);
16798                 this.dragCurrent.onDrag(e);
16799                 if(!this.dragCurrent.moveOnly){
16800                     this.fireEvents(e, false);
16801                 }
16802             }
16803
16804             this.stopEvent(e);
16805
16806             return true;
16807         },
16808
16809         /**
16810          * Iterates over all of the DragDrop elements to find ones we are
16811          * hovering over or dropping on
16812          * @method fireEvents
16813          * @param {Event} e the event
16814          * @param {boolean} isDrop is this a drop op or a mouseover op?
16815          * @private
16816          * @static
16817          */
16818         fireEvents: function(e, isDrop) {
16819             var dc = this.dragCurrent;
16820
16821             // If the user did the mouse up outside of the window, we could
16822             // get here even though we have ended the drag.
16823             if (!dc || dc.isLocked()) {
16824                 return;
16825             }
16826
16827             var pt = e.getPoint();
16828
16829             // cache the previous dragOver array
16830             var oldOvers = [];
16831
16832             var outEvts   = [];
16833             var overEvts  = [];
16834             var dropEvts  = [];
16835             var enterEvts = [];
16836
16837             // Check to see if the object(s) we were hovering over is no longer
16838             // being hovered over so we can fire the onDragOut event
16839             for (var i in this.dragOvers) {
16840
16841                 var ddo = this.dragOvers[i];
16842
16843                 if (! this.isTypeOfDD(ddo)) {
16844                     continue;
16845                 }
16846
16847                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16848                     outEvts.push( ddo );
16849                 }
16850
16851                 oldOvers[i] = true;
16852                 delete this.dragOvers[i];
16853             }
16854
16855             for (var sGroup in dc.groups) {
16856
16857                 if ("string" != typeof sGroup) {
16858                     continue;
16859                 }
16860
16861                 for (i in this.ids[sGroup]) {
16862                     var oDD = this.ids[sGroup][i];
16863                     if (! this.isTypeOfDD(oDD)) {
16864                         continue;
16865                     }
16866
16867                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16868                         if (this.isOverTarget(pt, oDD, this.mode)) {
16869                             // look for drop interactions
16870                             if (isDrop) {
16871                                 dropEvts.push( oDD );
16872                             // look for drag enter and drag over interactions
16873                             } else {
16874
16875                                 // initial drag over: dragEnter fires
16876                                 if (!oldOvers[oDD.id]) {
16877                                     enterEvts.push( oDD );
16878                                 // subsequent drag overs: dragOver fires
16879                                 } else {
16880                                     overEvts.push( oDD );
16881                                 }
16882
16883                                 this.dragOvers[oDD.id] = oDD;
16884                             }
16885                         }
16886                     }
16887                 }
16888             }
16889
16890             if (this.mode) {
16891                 if (outEvts.length) {
16892                     dc.b4DragOut(e, outEvts);
16893                     dc.onDragOut(e, outEvts);
16894                 }
16895
16896                 if (enterEvts.length) {
16897                     dc.onDragEnter(e, enterEvts);
16898                 }
16899
16900                 if (overEvts.length) {
16901                     dc.b4DragOver(e, overEvts);
16902                     dc.onDragOver(e, overEvts);
16903                 }
16904
16905                 if (dropEvts.length) {
16906                     dc.b4DragDrop(e, dropEvts);
16907                     dc.onDragDrop(e, dropEvts);
16908                 }
16909
16910             } else {
16911                 // fire dragout events
16912                 var len = 0;
16913                 for (i=0, len=outEvts.length; i<len; ++i) {
16914                     dc.b4DragOut(e, outEvts[i].id);
16915                     dc.onDragOut(e, outEvts[i].id);
16916                 }
16917
16918                 // fire enter events
16919                 for (i=0,len=enterEvts.length; i<len; ++i) {
16920                     // dc.b4DragEnter(e, oDD.id);
16921                     dc.onDragEnter(e, enterEvts[i].id);
16922                 }
16923
16924                 // fire over events
16925                 for (i=0,len=overEvts.length; i<len; ++i) {
16926                     dc.b4DragOver(e, overEvts[i].id);
16927                     dc.onDragOver(e, overEvts[i].id);
16928                 }
16929
16930                 // fire drop events
16931                 for (i=0, len=dropEvts.length; i<len; ++i) {
16932                     dc.b4DragDrop(e, dropEvts[i].id);
16933                     dc.onDragDrop(e, dropEvts[i].id);
16934                 }
16935
16936             }
16937
16938             // notify about a drop that did not find a target
16939             if (isDrop && !dropEvts.length) {
16940                 dc.onInvalidDrop(e);
16941             }
16942
16943         },
16944
16945         /**
16946          * Helper function for getting the best match from the list of drag
16947          * and drop objects returned by the drag and drop events when we are
16948          * in INTERSECT mode.  It returns either the first object that the
16949          * cursor is over, or the object that has the greatest overlap with
16950          * the dragged element.
16951          * @method getBestMatch
16952          * @param  {DragDrop[]} dds The array of drag and drop objects
16953          * targeted
16954          * @return {DragDrop}       The best single match
16955          * @static
16956          */
16957         getBestMatch: function(dds) {
16958             var winner = null;
16959             // Return null if the input is not what we expect
16960             //if (!dds || !dds.length || dds.length == 0) {
16961                // winner = null;
16962             // If there is only one item, it wins
16963             //} else if (dds.length == 1) {
16964
16965             var len = dds.length;
16966
16967             if (len == 1) {
16968                 winner = dds[0];
16969             } else {
16970                 // Loop through the targeted items
16971                 for (var i=0; i<len; ++i) {
16972                     var dd = dds[i];
16973                     // If the cursor is over the object, it wins.  If the
16974                     // cursor is over multiple matches, the first one we come
16975                     // to wins.
16976                     if (dd.cursorIsOver) {
16977                         winner = dd;
16978                         break;
16979                     // Otherwise the object with the most overlap wins
16980                     } else {
16981                         if (!winner ||
16982                             winner.overlap.getArea() < dd.overlap.getArea()) {
16983                             winner = dd;
16984                         }
16985                     }
16986                 }
16987             }
16988
16989             return winner;
16990         },
16991
16992         /**
16993          * Refreshes the cache of the top-left and bottom-right points of the
16994          * drag and drop objects in the specified group(s).  This is in the
16995          * format that is stored in the drag and drop instance, so typical
16996          * usage is:
16997          * <code>
16998          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16999          * </code>
17000          * Alternatively:
17001          * <code>
17002          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
17003          * </code>
17004          * @TODO this really should be an indexed array.  Alternatively this
17005          * method could accept both.
17006          * @method refreshCache
17007          * @param {Object} groups an associative array of groups to refresh
17008          * @static
17009          */
17010         refreshCache: function(groups) {
17011             for (var sGroup in groups) {
17012                 if ("string" != typeof sGroup) {
17013                     continue;
17014                 }
17015                 for (var i in this.ids[sGroup]) {
17016                     var oDD = this.ids[sGroup][i];
17017
17018                     if (this.isTypeOfDD(oDD)) {
17019                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
17020                         var loc = this.getLocation(oDD);
17021                         if (loc) {
17022                             this.locationCache[oDD.id] = loc;
17023                         } else {
17024                             delete this.locationCache[oDD.id];
17025                             // this will unregister the drag and drop object if
17026                             // the element is not in a usable state
17027                             // oDD.unreg();
17028                         }
17029                     }
17030                 }
17031             }
17032         },
17033
17034         /**
17035          * This checks to make sure an element exists and is in the DOM.  The
17036          * main purpose is to handle cases where innerHTML is used to remove
17037          * drag and drop objects from the DOM.  IE provides an 'unspecified
17038          * error' when trying to access the offsetParent of such an element
17039          * @method verifyEl
17040          * @param {HTMLElement} el the element to check
17041          * @return {boolean} true if the element looks usable
17042          * @static
17043          */
17044         verifyEl: function(el) {
17045             if (el) {
17046                 var parent;
17047                 if(Roo.isIE){
17048                     try{
17049                         parent = el.offsetParent;
17050                     }catch(e){}
17051                 }else{
17052                     parent = el.offsetParent;
17053                 }
17054                 if (parent) {
17055                     return true;
17056                 }
17057             }
17058
17059             return false;
17060         },
17061
17062         /**
17063          * Returns a Region object containing the drag and drop element's position
17064          * and size, including the padding configured for it
17065          * @method getLocation
17066          * @param {DragDrop} oDD the drag and drop object to get the
17067          *                       location for
17068          * @return {Roo.lib.Region} a Region object representing the total area
17069          *                             the element occupies, including any padding
17070          *                             the instance is configured for.
17071          * @static
17072          */
17073         getLocation: function(oDD) {
17074             if (! this.isTypeOfDD(oDD)) {
17075                 return null;
17076             }
17077
17078             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
17079
17080             try {
17081                 pos= Roo.lib.Dom.getXY(el);
17082             } catch (e) { }
17083
17084             if (!pos) {
17085                 return null;
17086             }
17087
17088             x1 = pos[0];
17089             x2 = x1 + el.offsetWidth;
17090             y1 = pos[1];
17091             y2 = y1 + el.offsetHeight;
17092
17093             t = y1 - oDD.padding[0];
17094             r = x2 + oDD.padding[1];
17095             b = y2 + oDD.padding[2];
17096             l = x1 - oDD.padding[3];
17097
17098             return new Roo.lib.Region( t, r, b, l );
17099         },
17100
17101         /**
17102          * Checks the cursor location to see if it over the target
17103          * @method isOverTarget
17104          * @param {Roo.lib.Point} pt The point to evaluate
17105          * @param {DragDrop} oTarget the DragDrop object we are inspecting
17106          * @return {boolean} true if the mouse is over the target
17107          * @private
17108          * @static
17109          */
17110         isOverTarget: function(pt, oTarget, intersect) {
17111             // use cache if available
17112             var loc = this.locationCache[oTarget.id];
17113             if (!loc || !this.useCache) {
17114                 loc = this.getLocation(oTarget);
17115                 this.locationCache[oTarget.id] = loc;
17116
17117             }
17118
17119             if (!loc) {
17120                 return false;
17121             }
17122
17123             oTarget.cursorIsOver = loc.contains( pt );
17124
17125             // DragDrop is using this as a sanity check for the initial mousedown
17126             // in this case we are done.  In POINT mode, if the drag obj has no
17127             // contraints, we are also done. Otherwise we need to evaluate the
17128             // location of the target as related to the actual location of the
17129             // dragged element.
17130             var dc = this.dragCurrent;
17131             if (!dc || !dc.getTargetCoord ||
17132                     (!intersect && !dc.constrainX && !dc.constrainY)) {
17133                 return oTarget.cursorIsOver;
17134             }
17135
17136             oTarget.overlap = null;
17137
17138             // Get the current location of the drag element, this is the
17139             // location of the mouse event less the delta that represents
17140             // where the original mousedown happened on the element.  We
17141             // need to consider constraints and ticks as well.
17142             var pos = dc.getTargetCoord(pt.x, pt.y);
17143
17144             var el = dc.getDragEl();
17145             var curRegion = new Roo.lib.Region( pos.y,
17146                                                    pos.x + el.offsetWidth,
17147                                                    pos.y + el.offsetHeight,
17148                                                    pos.x );
17149
17150             var overlap = curRegion.intersect(loc);
17151
17152             if (overlap) {
17153                 oTarget.overlap = overlap;
17154                 return (intersect) ? true : oTarget.cursorIsOver;
17155             } else {
17156                 return false;
17157             }
17158         },
17159
17160         /**
17161          * unload event handler
17162          * @method _onUnload
17163          * @private
17164          * @static
17165          */
17166         _onUnload: function(e, me) {
17167             Roo.dd.DragDropMgr.unregAll();
17168         },
17169
17170         /**
17171          * Cleans up the drag and drop events and objects.
17172          * @method unregAll
17173          * @private
17174          * @static
17175          */
17176         unregAll: function() {
17177
17178             if (this.dragCurrent) {
17179                 this.stopDrag();
17180                 this.dragCurrent = null;
17181             }
17182
17183             this._execOnAll("unreg", []);
17184
17185             for (i in this.elementCache) {
17186                 delete this.elementCache[i];
17187             }
17188
17189             this.elementCache = {};
17190             this.ids = {};
17191         },
17192
17193         /**
17194          * A cache of DOM elements
17195          * @property elementCache
17196          * @private
17197          * @static
17198          */
17199         elementCache: {},
17200
17201         /**
17202          * Get the wrapper for the DOM element specified
17203          * @method getElWrapper
17204          * @param {String} id the id of the element to get
17205          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
17206          * @private
17207          * @deprecated This wrapper isn't that useful
17208          * @static
17209          */
17210         getElWrapper: function(id) {
17211             var oWrapper = this.elementCache[id];
17212             if (!oWrapper || !oWrapper.el) {
17213                 oWrapper = this.elementCache[id] =
17214                     new this.ElementWrapper(Roo.getDom(id));
17215             }
17216             return oWrapper;
17217         },
17218
17219         /**
17220          * Returns the actual DOM element
17221          * @method getElement
17222          * @param {String} id the id of the elment to get
17223          * @return {Object} The element
17224          * @deprecated use Roo.getDom instead
17225          * @static
17226          */
17227         getElement: function(id) {
17228             return Roo.getDom(id);
17229         },
17230
17231         /**
17232          * Returns the style property for the DOM element (i.e.,
17233          * document.getElById(id).style)
17234          * @method getCss
17235          * @param {String} id the id of the elment to get
17236          * @return {Object} The style property of the element
17237          * @deprecated use Roo.getDom instead
17238          * @static
17239          */
17240         getCss: function(id) {
17241             var el = Roo.getDom(id);
17242             return (el) ? el.style : null;
17243         },
17244
17245         /**
17246          * Inner class for cached elements
17247          * @class DragDropMgr.ElementWrapper
17248          * @for DragDropMgr
17249          * @private
17250          * @deprecated
17251          */
17252         ElementWrapper: function(el) {
17253                 /**
17254                  * The element
17255                  * @property el
17256                  */
17257                 this.el = el || null;
17258                 /**
17259                  * The element id
17260                  * @property id
17261                  */
17262                 this.id = this.el && el.id;
17263                 /**
17264                  * A reference to the style property
17265                  * @property css
17266                  */
17267                 this.css = this.el && el.style;
17268             },
17269
17270         /**
17271          * Returns the X position of an html element
17272          * @method getPosX
17273          * @param el the element for which to get the position
17274          * @return {int} the X coordinate
17275          * @for DragDropMgr
17276          * @deprecated use Roo.lib.Dom.getX instead
17277          * @static
17278          */
17279         getPosX: function(el) {
17280             return Roo.lib.Dom.getX(el);
17281         },
17282
17283         /**
17284          * Returns the Y position of an html element
17285          * @method getPosY
17286          * @param el the element for which to get the position
17287          * @return {int} the Y coordinate
17288          * @deprecated use Roo.lib.Dom.getY instead
17289          * @static
17290          */
17291         getPosY: function(el) {
17292             return Roo.lib.Dom.getY(el);
17293         },
17294
17295         /**
17296          * Swap two nodes.  In IE, we use the native method, for others we
17297          * emulate the IE behavior
17298          * @method swapNode
17299          * @param n1 the first node to swap
17300          * @param n2 the other node to swap
17301          * @static
17302          */
17303         swapNode: function(n1, n2) {
17304             if (n1.swapNode) {
17305                 n1.swapNode(n2);
17306             } else {
17307                 var p = n2.parentNode;
17308                 var s = n2.nextSibling;
17309
17310                 if (s == n1) {
17311                     p.insertBefore(n1, n2);
17312                 } else if (n2 == n1.nextSibling) {
17313                     p.insertBefore(n2, n1);
17314                 } else {
17315                     n1.parentNode.replaceChild(n2, n1);
17316                     p.insertBefore(n1, s);
17317                 }
17318             }
17319         },
17320
17321         /**
17322          * Returns the current scroll position
17323          * @method getScroll
17324          * @private
17325          * @static
17326          */
17327         getScroll: function () {
17328             var t, l, dde=document.documentElement, db=document.body;
17329             if (dde && (dde.scrollTop || dde.scrollLeft)) {
17330                 t = dde.scrollTop;
17331                 l = dde.scrollLeft;
17332             } else if (db) {
17333                 t = db.scrollTop;
17334                 l = db.scrollLeft;
17335             } else {
17336
17337             }
17338             return { top: t, left: l };
17339         },
17340
17341         /**
17342          * Returns the specified element style property
17343          * @method getStyle
17344          * @param {HTMLElement} el          the element
17345          * @param {string}      styleProp   the style property
17346          * @return {string} The value of the style property
17347          * @deprecated use Roo.lib.Dom.getStyle
17348          * @static
17349          */
17350         getStyle: function(el, styleProp) {
17351             return Roo.fly(el).getStyle(styleProp);
17352         },
17353
17354         /**
17355          * Gets the scrollTop
17356          * @method getScrollTop
17357          * @return {int} the document's scrollTop
17358          * @static
17359          */
17360         getScrollTop: function () { return this.getScroll().top; },
17361
17362         /**
17363          * Gets the scrollLeft
17364          * @method getScrollLeft
17365          * @return {int} the document's scrollTop
17366          * @static
17367          */
17368         getScrollLeft: function () { return this.getScroll().left; },
17369
17370         /**
17371          * Sets the x/y position of an element to the location of the
17372          * target element.
17373          * @method moveToEl
17374          * @param {HTMLElement} moveEl      The element to move
17375          * @param {HTMLElement} targetEl    The position reference element
17376          * @static
17377          */
17378         moveToEl: function (moveEl, targetEl) {
17379             var aCoord = Roo.lib.Dom.getXY(targetEl);
17380             Roo.lib.Dom.setXY(moveEl, aCoord);
17381         },
17382
17383         /**
17384          * Numeric array sort function
17385          * @method numericSort
17386          * @static
17387          */
17388         numericSort: function(a, b) { return (a - b); },
17389
17390         /**
17391          * Internal counter
17392          * @property _timeoutCount
17393          * @private
17394          * @static
17395          */
17396         _timeoutCount: 0,
17397
17398         /**
17399          * Trying to make the load order less important.  Without this we get
17400          * an error if this file is loaded before the Event Utility.
17401          * @method _addListeners
17402          * @private
17403          * @static
17404          */
17405         _addListeners: function() {
17406             var DDM = Roo.dd.DDM;
17407             if ( Roo.lib.Event && document ) {
17408                 DDM._onLoad();
17409             } else {
17410                 if (DDM._timeoutCount > 2000) {
17411                 } else {
17412                     setTimeout(DDM._addListeners, 10);
17413                     if (document && document.body) {
17414                         DDM._timeoutCount += 1;
17415                     }
17416                 }
17417             }
17418         },
17419
17420         /**
17421          * Recursively searches the immediate parent and all child nodes for
17422          * the handle element in order to determine wheter or not it was
17423          * clicked.
17424          * @method handleWasClicked
17425          * @param node the html element to inspect
17426          * @static
17427          */
17428         handleWasClicked: function(node, id) {
17429             if (this.isHandle(id, node.id)) {
17430                 return true;
17431             } else {
17432                 // check to see if this is a text node child of the one we want
17433                 var p = node.parentNode;
17434
17435                 while (p) {
17436                     if (this.isHandle(id, p.id)) {
17437                         return true;
17438                     } else {
17439                         p = p.parentNode;
17440                     }
17441                 }
17442             }
17443
17444             return false;
17445         }
17446
17447     };
17448
17449 }();
17450
17451 // shorter alias, save a few bytes
17452 Roo.dd.DDM = Roo.dd.DragDropMgr;
17453 Roo.dd.DDM._addListeners();
17454
17455 }/*
17456  * Based on:
17457  * Ext JS Library 1.1.1
17458  * Copyright(c) 2006-2007, Ext JS, LLC.
17459  *
17460  * Originally Released Under LGPL - original licence link has changed is not relivant.
17461  *
17462  * Fork - LGPL
17463  * <script type="text/javascript">
17464  */
17465
17466 /**
17467  * @class Roo.dd.DD
17468  * A DragDrop implementation where the linked element follows the
17469  * mouse cursor during a drag.
17470  * @extends Roo.dd.DragDrop
17471  * @constructor
17472  * @param {String} id the id of the linked element
17473  * @param {String} sGroup the group of related DragDrop items
17474  * @param {object} config an object containing configurable attributes
17475  *                Valid properties for DD:
17476  *                    scroll
17477  */
17478 Roo.dd.DD = function(id, sGroup, config) {
17479     if (id) {
17480         this.init(id, sGroup, config);
17481     }
17482 };
17483
17484 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
17485
17486     /**
17487      * When set to true, the utility automatically tries to scroll the browser
17488      * window wehn a drag and drop element is dragged near the viewport boundary.
17489      * Defaults to true.
17490      * @property scroll
17491      * @type boolean
17492      */
17493     scroll: true,
17494
17495     /**
17496      * Sets the pointer offset to the distance between the linked element's top
17497      * left corner and the location the element was clicked
17498      * @method autoOffset
17499      * @param {int} iPageX the X coordinate of the click
17500      * @param {int} iPageY the Y coordinate of the click
17501      */
17502     autoOffset: function(iPageX, iPageY) {
17503         var x = iPageX - this.startPageX;
17504         var y = iPageY - this.startPageY;
17505         this.setDelta(x, y);
17506     },
17507
17508     /**
17509      * Sets the pointer offset.  You can call this directly to force the
17510      * offset to be in a particular location (e.g., pass in 0,0 to set it
17511      * to the center of the object)
17512      * @method setDelta
17513      * @param {int} iDeltaX the distance from the left
17514      * @param {int} iDeltaY the distance from the top
17515      */
17516     setDelta: function(iDeltaX, iDeltaY) {
17517         this.deltaX = iDeltaX;
17518         this.deltaY = iDeltaY;
17519     },
17520
17521     /**
17522      * Sets the drag element to the location of the mousedown or click event,
17523      * maintaining the cursor location relative to the location on the element
17524      * that was clicked.  Override this if you want to place the element in a
17525      * location other than where the cursor is.
17526      * @method setDragElPos
17527      * @param {int} iPageX the X coordinate of the mousedown or drag event
17528      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17529      */
17530     setDragElPos: function(iPageX, iPageY) {
17531         // the first time we do this, we are going to check to make sure
17532         // the element has css positioning
17533
17534         var el = this.getDragEl();
17535         this.alignElWithMouse(el, iPageX, iPageY);
17536     },
17537
17538     /**
17539      * Sets the element to the location of the mousedown or click event,
17540      * maintaining the cursor location relative to the location on the element
17541      * that was clicked.  Override this if you want to place the element in a
17542      * location other than where the cursor is.
17543      * @method alignElWithMouse
17544      * @param {HTMLElement} el the element to move
17545      * @param {int} iPageX the X coordinate of the mousedown or drag event
17546      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17547      */
17548     alignElWithMouse: function(el, iPageX, iPageY) {
17549         var oCoord = this.getTargetCoord(iPageX, iPageY);
17550         var fly = el.dom ? el : Roo.fly(el);
17551         if (!this.deltaSetXY) {
17552             var aCoord = [oCoord.x, oCoord.y];
17553             fly.setXY(aCoord);
17554             var newLeft = fly.getLeft(true);
17555             var newTop  = fly.getTop(true);
17556             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17557         } else {
17558             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17559         }
17560
17561         this.cachePosition(oCoord.x, oCoord.y);
17562         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17563         return oCoord;
17564     },
17565
17566     /**
17567      * Saves the most recent position so that we can reset the constraints and
17568      * tick marks on-demand.  We need to know this so that we can calculate the
17569      * number of pixels the element is offset from its original position.
17570      * @method cachePosition
17571      * @param iPageX the current x position (optional, this just makes it so we
17572      * don't have to look it up again)
17573      * @param iPageY the current y position (optional, this just makes it so we
17574      * don't have to look it up again)
17575      */
17576     cachePosition: function(iPageX, iPageY) {
17577         if (iPageX) {
17578             this.lastPageX = iPageX;
17579             this.lastPageY = iPageY;
17580         } else {
17581             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17582             this.lastPageX = aCoord[0];
17583             this.lastPageY = aCoord[1];
17584         }
17585     },
17586
17587     /**
17588      * Auto-scroll the window if the dragged object has been moved beyond the
17589      * visible window boundary.
17590      * @method autoScroll
17591      * @param {int} x the drag element's x position
17592      * @param {int} y the drag element's y position
17593      * @param {int} h the height of the drag element
17594      * @param {int} w the width of the drag element
17595      * @private
17596      */
17597     autoScroll: function(x, y, h, w) {
17598
17599         if (this.scroll) {
17600             // The client height
17601             var clientH = Roo.lib.Dom.getViewWidth();
17602
17603             // The client width
17604             var clientW = Roo.lib.Dom.getViewHeight();
17605
17606             // The amt scrolled down
17607             var st = this.DDM.getScrollTop();
17608
17609             // The amt scrolled right
17610             var sl = this.DDM.getScrollLeft();
17611
17612             // Location of the bottom of the element
17613             var bot = h + y;
17614
17615             // Location of the right of the element
17616             var right = w + x;
17617
17618             // The distance from the cursor to the bottom of the visible area,
17619             // adjusted so that we don't scroll if the cursor is beyond the
17620             // element drag constraints
17621             var toBot = (clientH + st - y - this.deltaY);
17622
17623             // The distance from the cursor to the right of the visible area
17624             var toRight = (clientW + sl - x - this.deltaX);
17625
17626
17627             // How close to the edge the cursor must be before we scroll
17628             // var thresh = (document.all) ? 100 : 40;
17629             var thresh = 40;
17630
17631             // How many pixels to scroll per autoscroll op.  This helps to reduce
17632             // clunky scrolling. IE is more sensitive about this ... it needs this
17633             // value to be higher.
17634             var scrAmt = (document.all) ? 80 : 30;
17635
17636             // Scroll down if we are near the bottom of the visible page and the
17637             // obj extends below the crease
17638             if ( bot > clientH && toBot < thresh ) {
17639                 window.scrollTo(sl, st + scrAmt);
17640             }
17641
17642             // Scroll up if the window is scrolled down and the top of the object
17643             // goes above the top border
17644             if ( y < st && st > 0 && y - st < thresh ) {
17645                 window.scrollTo(sl, st - scrAmt);
17646             }
17647
17648             // Scroll right if the obj is beyond the right border and the cursor is
17649             // near the border.
17650             if ( right > clientW && toRight < thresh ) {
17651                 window.scrollTo(sl + scrAmt, st);
17652             }
17653
17654             // Scroll left if the window has been scrolled to the right and the obj
17655             // extends past the left border
17656             if ( x < sl && sl > 0 && x - sl < thresh ) {
17657                 window.scrollTo(sl - scrAmt, st);
17658             }
17659         }
17660     },
17661
17662     /**
17663      * Finds the location the element should be placed if we want to move
17664      * it to where the mouse location less the click offset would place us.
17665      * @method getTargetCoord
17666      * @param {int} iPageX the X coordinate of the click
17667      * @param {int} iPageY the Y coordinate of the click
17668      * @return an object that contains the coordinates (Object.x and Object.y)
17669      * @private
17670      */
17671     getTargetCoord: function(iPageX, iPageY) {
17672
17673
17674         var x = iPageX - this.deltaX;
17675         var y = iPageY - this.deltaY;
17676
17677         if (this.constrainX) {
17678             if (x < this.minX) { x = this.minX; }
17679             if (x > this.maxX) { x = this.maxX; }
17680         }
17681
17682         if (this.constrainY) {
17683             if (y < this.minY) { y = this.minY; }
17684             if (y > this.maxY) { y = this.maxY; }
17685         }
17686
17687         x = this.getTick(x, this.xTicks);
17688         y = this.getTick(y, this.yTicks);
17689
17690
17691         return {x:x, y:y};
17692     },
17693
17694     /*
17695      * Sets up config options specific to this class. Overrides
17696      * Roo.dd.DragDrop, but all versions of this method through the
17697      * inheritance chain are called
17698      */
17699     applyConfig: function() {
17700         Roo.dd.DD.superclass.applyConfig.call(this);
17701         this.scroll = (this.config.scroll !== false);
17702     },
17703
17704     /*
17705      * Event that fires prior to the onMouseDown event.  Overrides
17706      * Roo.dd.DragDrop.
17707      */
17708     b4MouseDown: function(e) {
17709         // this.resetConstraints();
17710         this.autoOffset(e.getPageX(),
17711                             e.getPageY());
17712     },
17713
17714     /*
17715      * Event that fires prior to the onDrag event.  Overrides
17716      * Roo.dd.DragDrop.
17717      */
17718     b4Drag: function(e) {
17719         this.setDragElPos(e.getPageX(),
17720                             e.getPageY());
17721     },
17722
17723     toString: function() {
17724         return ("DD " + this.id);
17725     }
17726
17727     //////////////////////////////////////////////////////////////////////////
17728     // Debugging ygDragDrop events that can be overridden
17729     //////////////////////////////////////////////////////////////////////////
17730     /*
17731     startDrag: function(x, y) {
17732     },
17733
17734     onDrag: function(e) {
17735     },
17736
17737     onDragEnter: function(e, id) {
17738     },
17739
17740     onDragOver: function(e, id) {
17741     },
17742
17743     onDragOut: function(e, id) {
17744     },
17745
17746     onDragDrop: function(e, id) {
17747     },
17748
17749     endDrag: function(e) {
17750     }
17751
17752     */
17753
17754 });/*
17755  * Based on:
17756  * Ext JS Library 1.1.1
17757  * Copyright(c) 2006-2007, Ext JS, LLC.
17758  *
17759  * Originally Released Under LGPL - original licence link has changed is not relivant.
17760  *
17761  * Fork - LGPL
17762  * <script type="text/javascript">
17763  */
17764
17765 /**
17766  * @class Roo.dd.DDProxy
17767  * A DragDrop implementation that inserts an empty, bordered div into
17768  * the document that follows the cursor during drag operations.  At the time of
17769  * the click, the frame div is resized to the dimensions of the linked html
17770  * element, and moved to the exact location of the linked element.
17771  *
17772  * References to the "frame" element refer to the single proxy element that
17773  * was created to be dragged in place of all DDProxy elements on the
17774  * page.
17775  *
17776  * @extends Roo.dd.DD
17777  * @constructor
17778  * @param {String} id the id of the linked html element
17779  * @param {String} sGroup the group of related DragDrop objects
17780  * @param {object} config an object containing configurable attributes
17781  *                Valid properties for DDProxy in addition to those in DragDrop:
17782  *                   resizeFrame, centerFrame, dragElId
17783  */
17784 Roo.dd.DDProxy = function(id, sGroup, config) {
17785     if (id) {
17786         this.init(id, sGroup, config);
17787         this.initFrame();
17788     }
17789 };
17790
17791 /**
17792  * The default drag frame div id
17793  * @property Roo.dd.DDProxy.dragElId
17794  * @type String
17795  * @static
17796  */
17797 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17798
17799 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17800
17801     /**
17802      * By default we resize the drag frame to be the same size as the element
17803      * we want to drag (this is to get the frame effect).  We can turn it off
17804      * if we want a different behavior.
17805      * @property resizeFrame
17806      * @type boolean
17807      */
17808     resizeFrame: true,
17809
17810     /**
17811      * By default the frame is positioned exactly where the drag element is, so
17812      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17813      * you do not have constraints on the obj is to have the drag frame centered
17814      * around the cursor.  Set centerFrame to true for this effect.
17815      * @property centerFrame
17816      * @type boolean
17817      */
17818     centerFrame: false,
17819
17820     /**
17821      * Creates the proxy element if it does not yet exist
17822      * @method createFrame
17823      */
17824     createFrame: function() {
17825         var self = this;
17826         var body = document.body;
17827
17828         if (!body || !body.firstChild) {
17829             setTimeout( function() { self.createFrame(); }, 50 );
17830             return;
17831         }
17832
17833         var div = this.getDragEl();
17834
17835         if (!div) {
17836             div    = document.createElement("div");
17837             div.id = this.dragElId;
17838             var s  = div.style;
17839
17840             s.position   = "absolute";
17841             s.visibility = "hidden";
17842             s.cursor     = "move";
17843             s.border     = "2px solid #aaa";
17844             s.zIndex     = 999;
17845
17846             // appendChild can blow up IE if invoked prior to the window load event
17847             // while rendering a table.  It is possible there are other scenarios
17848             // that would cause this to happen as well.
17849             body.insertBefore(div, body.firstChild);
17850         }
17851     },
17852
17853     /**
17854      * Initialization for the drag frame element.  Must be called in the
17855      * constructor of all subclasses
17856      * @method initFrame
17857      */
17858     initFrame: function() {
17859         this.createFrame();
17860     },
17861
17862     applyConfig: function() {
17863         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17864
17865         this.resizeFrame = (this.config.resizeFrame !== false);
17866         this.centerFrame = (this.config.centerFrame);
17867         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17868     },
17869
17870     /**
17871      * Resizes the drag frame to the dimensions of the clicked object, positions
17872      * it over the object, and finally displays it
17873      * @method showFrame
17874      * @param {int} iPageX X click position
17875      * @param {int} iPageY Y click position
17876      * @private
17877      */
17878     showFrame: function(iPageX, iPageY) {
17879         var el = this.getEl();
17880         var dragEl = this.getDragEl();
17881         var s = dragEl.style;
17882
17883         this._resizeProxy();
17884
17885         if (this.centerFrame) {
17886             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17887                            Math.round(parseInt(s.height, 10)/2) );
17888         }
17889
17890         this.setDragElPos(iPageX, iPageY);
17891
17892         Roo.fly(dragEl).show();
17893     },
17894
17895     /**
17896      * The proxy is automatically resized to the dimensions of the linked
17897      * element when a drag is initiated, unless resizeFrame is set to false
17898      * @method _resizeProxy
17899      * @private
17900      */
17901     _resizeProxy: function() {
17902         if (this.resizeFrame) {
17903             var el = this.getEl();
17904             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17905         }
17906     },
17907
17908     // overrides Roo.dd.DragDrop
17909     b4MouseDown: function(e) {
17910         var x = e.getPageX();
17911         var y = e.getPageY();
17912         this.autoOffset(x, y);
17913         this.setDragElPos(x, y);
17914     },
17915
17916     // overrides Roo.dd.DragDrop
17917     b4StartDrag: function(x, y) {
17918         // show the drag frame
17919         this.showFrame(x, y);
17920     },
17921
17922     // overrides Roo.dd.DragDrop
17923     b4EndDrag: function(e) {
17924         Roo.fly(this.getDragEl()).hide();
17925     },
17926
17927     // overrides Roo.dd.DragDrop
17928     // By default we try to move the element to the last location of the frame.
17929     // This is so that the default behavior mirrors that of Roo.dd.DD.
17930     endDrag: function(e) {
17931
17932         var lel = this.getEl();
17933         var del = this.getDragEl();
17934
17935         // Show the drag frame briefly so we can get its position
17936         del.style.visibility = "";
17937
17938         this.beforeMove();
17939         // Hide the linked element before the move to get around a Safari
17940         // rendering bug.
17941         lel.style.visibility = "hidden";
17942         Roo.dd.DDM.moveToEl(lel, del);
17943         del.style.visibility = "hidden";
17944         lel.style.visibility = "";
17945
17946         this.afterDrag();
17947     },
17948
17949     beforeMove : function(){
17950
17951     },
17952
17953     afterDrag : function(){
17954
17955     },
17956
17957     toString: function() {
17958         return ("DDProxy " + this.id);
17959     }
17960
17961 });
17962 /*
17963  * Based on:
17964  * Ext JS Library 1.1.1
17965  * Copyright(c) 2006-2007, Ext JS, LLC.
17966  *
17967  * Originally Released Under LGPL - original licence link has changed is not relivant.
17968  *
17969  * Fork - LGPL
17970  * <script type="text/javascript">
17971  */
17972
17973  /**
17974  * @class Roo.dd.DDTarget
17975  * A DragDrop implementation that does not move, but can be a drop
17976  * target.  You would get the same result by simply omitting implementation
17977  * for the event callbacks, but this way we reduce the processing cost of the
17978  * event listener and the callbacks.
17979  * @extends Roo.dd.DragDrop
17980  * @constructor
17981  * @param {String} id the id of the element that is a drop target
17982  * @param {String} sGroup the group of related DragDrop objects
17983  * @param {object} config an object containing configurable attributes
17984  *                 Valid properties for DDTarget in addition to those in
17985  *                 DragDrop:
17986  *                    none
17987  */
17988 Roo.dd.DDTarget = function(id, sGroup, config) {
17989     if (id) {
17990         this.initTarget(id, sGroup, config);
17991     }
17992     if (config.listeners || config.events) { 
17993        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17994             listeners : config.listeners || {}, 
17995             events : config.events || {} 
17996         });    
17997     }
17998 };
17999
18000 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
18001 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
18002     toString: function() {
18003         return ("DDTarget " + this.id);
18004     }
18005 });
18006 /*
18007  * Based on:
18008  * Ext JS Library 1.1.1
18009  * Copyright(c) 2006-2007, Ext JS, LLC.
18010  *
18011  * Originally Released Under LGPL - original licence link has changed is not relivant.
18012  *
18013  * Fork - LGPL
18014  * <script type="text/javascript">
18015  */
18016  
18017
18018 /**
18019  * @class Roo.dd.ScrollManager
18020  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
18021  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
18022  * @singleton
18023  */
18024 Roo.dd.ScrollManager = function(){
18025     var ddm = Roo.dd.DragDropMgr;
18026     var els = {};
18027     var dragEl = null;
18028     var proc = {};
18029     
18030     
18031     
18032     var onStop = function(e){
18033         dragEl = null;
18034         clearProc();
18035     };
18036     
18037     var triggerRefresh = function(){
18038         if(ddm.dragCurrent){
18039              ddm.refreshCache(ddm.dragCurrent.groups);
18040         }
18041     };
18042     
18043     var doScroll = function(){
18044         if(ddm.dragCurrent){
18045             var dds = Roo.dd.ScrollManager;
18046             if(!dds.animate){
18047                 if(proc.el.scroll(proc.dir, dds.increment)){
18048                     triggerRefresh();
18049                 }
18050             }else{
18051                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
18052             }
18053         }
18054     };
18055     
18056     var clearProc = function(){
18057         if(proc.id){
18058             clearInterval(proc.id);
18059         }
18060         proc.id = 0;
18061         proc.el = null;
18062         proc.dir = "";
18063     };
18064     
18065     var startProc = function(el, dir){
18066          Roo.log('scroll startproc');
18067         clearProc();
18068         proc.el = el;
18069         proc.dir = dir;
18070         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
18071     };
18072     
18073     var onFire = function(e, isDrop){
18074        
18075         if(isDrop || !ddm.dragCurrent){ return; }
18076         var dds = Roo.dd.ScrollManager;
18077         if(!dragEl || dragEl != ddm.dragCurrent){
18078             dragEl = ddm.dragCurrent;
18079             // refresh regions on drag start
18080             dds.refreshCache();
18081         }
18082         
18083         var xy = Roo.lib.Event.getXY(e);
18084         var pt = new Roo.lib.Point(xy[0], xy[1]);
18085         for(var id in els){
18086             var el = els[id], r = el._region;
18087             if(r && r.contains(pt) && el.isScrollable()){
18088                 if(r.bottom - pt.y <= dds.thresh){
18089                     if(proc.el != el){
18090                         startProc(el, "down");
18091                     }
18092                     return;
18093                 }else if(r.right - pt.x <= dds.thresh){
18094                     if(proc.el != el){
18095                         startProc(el, "left");
18096                     }
18097                     return;
18098                 }else if(pt.y - r.top <= dds.thresh){
18099                     if(proc.el != el){
18100                         startProc(el, "up");
18101                     }
18102                     return;
18103                 }else if(pt.x - r.left <= dds.thresh){
18104                     if(proc.el != el){
18105                         startProc(el, "right");
18106                     }
18107                     return;
18108                 }
18109             }
18110         }
18111         clearProc();
18112     };
18113     
18114     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
18115     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
18116     
18117     return {
18118         /**
18119          * Registers new overflow element(s) to auto scroll
18120          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
18121          */
18122         register : function(el){
18123             if(el instanceof Array){
18124                 for(var i = 0, len = el.length; i < len; i++) {
18125                         this.register(el[i]);
18126                 }
18127             }else{
18128                 el = Roo.get(el);
18129                 els[el.id] = el;
18130             }
18131             Roo.dd.ScrollManager.els = els;
18132         },
18133         
18134         /**
18135          * Unregisters overflow element(s) so they are no longer scrolled
18136          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
18137          */
18138         unregister : function(el){
18139             if(el instanceof Array){
18140                 for(var i = 0, len = el.length; i < len; i++) {
18141                         this.unregister(el[i]);
18142                 }
18143             }else{
18144                 el = Roo.get(el);
18145                 delete els[el.id];
18146             }
18147         },
18148         
18149         /**
18150          * The number of pixels from the edge of a container the pointer needs to be to 
18151          * trigger scrolling (defaults to 25)
18152          * @type Number
18153          */
18154         thresh : 25,
18155         
18156         /**
18157          * The number of pixels to scroll in each scroll increment (defaults to 50)
18158          * @type Number
18159          */
18160         increment : 100,
18161         
18162         /**
18163          * The frequency of scrolls in milliseconds (defaults to 500)
18164          * @type Number
18165          */
18166         frequency : 500,
18167         
18168         /**
18169          * True to animate the scroll (defaults to true)
18170          * @type Boolean
18171          */
18172         animate: true,
18173         
18174         /**
18175          * The animation duration in seconds - 
18176          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
18177          * @type Number
18178          */
18179         animDuration: .4,
18180         
18181         /**
18182          * Manually trigger a cache refresh.
18183          */
18184         refreshCache : function(){
18185             for(var id in els){
18186                 if(typeof els[id] == 'object'){ // for people extending the object prototype
18187                     els[id]._region = els[id].getRegion();
18188                 }
18189             }
18190         }
18191     };
18192 }();/*
18193  * Based on:
18194  * Ext JS Library 1.1.1
18195  * Copyright(c) 2006-2007, Ext JS, LLC.
18196  *
18197  * Originally Released Under LGPL - original licence link has changed is not relivant.
18198  *
18199  * Fork - LGPL
18200  * <script type="text/javascript">
18201  */
18202  
18203
18204 /**
18205  * @class Roo.dd.Registry
18206  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
18207  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
18208  * @singleton
18209  */
18210 Roo.dd.Registry = function(){
18211     var elements = {}; 
18212     var handles = {}; 
18213     var autoIdSeed = 0;
18214
18215     var getId = function(el, autogen){
18216         if(typeof el == "string"){
18217             return el;
18218         }
18219         var id = el.id;
18220         if(!id && autogen !== false){
18221             id = "roodd-" + (++autoIdSeed);
18222             el.id = id;
18223         }
18224         return id;
18225     };
18226     
18227     return {
18228     /**
18229      * Register a drag drop element
18230      * @param {String|HTMLElement} element The id or DOM node to register
18231      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
18232      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
18233      * knows how to interpret, plus there are some specific properties known to the Registry that should be
18234      * populated in the data object (if applicable):
18235      * <pre>
18236 Value      Description<br />
18237 ---------  ------------------------------------------<br />
18238 handles    Array of DOM nodes that trigger dragging<br />
18239            for the element being registered<br />
18240 isHandle   True if the element passed in triggers<br />
18241            dragging itself, else false
18242 </pre>
18243      */
18244         register : function(el, data){
18245             data = data || {};
18246             if(typeof el == "string"){
18247                 el = document.getElementById(el);
18248             }
18249             data.ddel = el;
18250             elements[getId(el)] = data;
18251             if(data.isHandle !== false){
18252                 handles[data.ddel.id] = data;
18253             }
18254             if(data.handles){
18255                 var hs = data.handles;
18256                 for(var i = 0, len = hs.length; i < len; i++){
18257                         handles[getId(hs[i])] = data;
18258                 }
18259             }
18260         },
18261
18262     /**
18263      * Unregister a drag drop element
18264      * @param {String|HTMLElement}  element The id or DOM node to unregister
18265      */
18266         unregister : function(el){
18267             var id = getId(el, false);
18268             var data = elements[id];
18269             if(data){
18270                 delete elements[id];
18271                 if(data.handles){
18272                     var hs = data.handles;
18273                     for(var i = 0, len = hs.length; i < len; i++){
18274                         delete handles[getId(hs[i], false)];
18275                     }
18276                 }
18277             }
18278         },
18279
18280     /**
18281      * Returns the handle registered for a DOM Node by id
18282      * @param {String|HTMLElement} id The DOM node or id to look up
18283      * @return {Object} handle The custom handle data
18284      */
18285         getHandle : function(id){
18286             if(typeof id != "string"){ // must be element?
18287                 id = id.id;
18288             }
18289             return handles[id];
18290         },
18291
18292     /**
18293      * Returns the handle that is registered for the DOM node that is the target of the event
18294      * @param {Event} e The event
18295      * @return {Object} handle The custom handle data
18296      */
18297         getHandleFromEvent : function(e){
18298             var t = Roo.lib.Event.getTarget(e);
18299             return t ? handles[t.id] : null;
18300         },
18301
18302     /**
18303      * Returns a custom data object that is registered for a DOM node by id
18304      * @param {String|HTMLElement} id The DOM node or id to look up
18305      * @return {Object} data The custom data
18306      */
18307         getTarget : function(id){
18308             if(typeof id != "string"){ // must be element?
18309                 id = id.id;
18310             }
18311             return elements[id];
18312         },
18313
18314     /**
18315      * Returns a custom data object that is registered for the DOM node that is the target of the event
18316      * @param {Event} e The event
18317      * @return {Object} data The custom data
18318      */
18319         getTargetFromEvent : function(e){
18320             var t = Roo.lib.Event.getTarget(e);
18321             return t ? elements[t.id] || handles[t.id] : null;
18322         }
18323     };
18324 }();/*
18325  * Based on:
18326  * Ext JS Library 1.1.1
18327  * Copyright(c) 2006-2007, Ext JS, LLC.
18328  *
18329  * Originally Released Under LGPL - original licence link has changed is not relivant.
18330  *
18331  * Fork - LGPL
18332  * <script type="text/javascript">
18333  */
18334  
18335
18336 /**
18337  * @class Roo.dd.StatusProxy
18338  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
18339  * default drag proxy used by all Roo.dd components.
18340  * @constructor
18341  * @param {Object} config
18342  */
18343 Roo.dd.StatusProxy = function(config){
18344     Roo.apply(this, config);
18345     this.id = this.id || Roo.id();
18346     this.el = new Roo.Layer({
18347         dh: {
18348             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
18349                 {tag: "div", cls: "x-dd-drop-icon"},
18350                 {tag: "div", cls: "x-dd-drag-ghost"}
18351             ]
18352         }, 
18353         shadow: !config || config.shadow !== false
18354     });
18355     this.ghost = Roo.get(this.el.dom.childNodes[1]);
18356     this.dropStatus = this.dropNotAllowed;
18357 };
18358
18359 Roo.dd.StatusProxy.prototype = {
18360     /**
18361      * @cfg {String} dropAllowed
18362      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
18363      */
18364     dropAllowed : "x-dd-drop-ok",
18365     /**
18366      * @cfg {String} dropNotAllowed
18367      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
18368      */
18369     dropNotAllowed : "x-dd-drop-nodrop",
18370
18371     /**
18372      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
18373      * over the current target element.
18374      * @param {String} cssClass The css class for the new drop status indicator image
18375      */
18376     setStatus : function(cssClass){
18377         cssClass = cssClass || this.dropNotAllowed;
18378         if(this.dropStatus != cssClass){
18379             this.el.replaceClass(this.dropStatus, cssClass);
18380             this.dropStatus = cssClass;
18381         }
18382     },
18383
18384     /**
18385      * Resets the status indicator to the default dropNotAllowed value
18386      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
18387      */
18388     reset : function(clearGhost){
18389         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
18390         this.dropStatus = this.dropNotAllowed;
18391         if(clearGhost){
18392             this.ghost.update("");
18393         }
18394     },
18395
18396     /**
18397      * Updates the contents of the ghost element
18398      * @param {String} html The html that will replace the current innerHTML of the ghost element
18399      */
18400     update : function(html){
18401         if(typeof html == "string"){
18402             this.ghost.update(html);
18403         }else{
18404             this.ghost.update("");
18405             html.style.margin = "0";
18406             this.ghost.dom.appendChild(html);
18407         }
18408         // ensure float = none set?? cant remember why though.
18409         var el = this.ghost.dom.firstChild;
18410                 if(el){
18411                         Roo.fly(el).setStyle('float', 'none');
18412                 }
18413     },
18414     
18415     /**
18416      * Returns the underlying proxy {@link Roo.Layer}
18417      * @return {Roo.Layer} el
18418     */
18419     getEl : function(){
18420         return this.el;
18421     },
18422
18423     /**
18424      * Returns the ghost element
18425      * @return {Roo.Element} el
18426      */
18427     getGhost : function(){
18428         return this.ghost;
18429     },
18430
18431     /**
18432      * Hides the proxy
18433      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
18434      */
18435     hide : function(clear){
18436         this.el.hide();
18437         if(clear){
18438             this.reset(true);
18439         }
18440     },
18441
18442     /**
18443      * Stops the repair animation if it's currently running
18444      */
18445     stop : function(){
18446         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
18447             this.anim.stop();
18448         }
18449     },
18450
18451     /**
18452      * Displays this proxy
18453      */
18454     show : function(){
18455         this.el.show();
18456     },
18457
18458     /**
18459      * Force the Layer to sync its shadow and shim positions to the element
18460      */
18461     sync : function(){
18462         this.el.sync();
18463     },
18464
18465     /**
18466      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
18467      * invalid drop operation by the item being dragged.
18468      * @param {Array} xy The XY position of the element ([x, y])
18469      * @param {Function} callback The function to call after the repair is complete
18470      * @param {Object} scope The scope in which to execute the callback
18471      */
18472     repair : function(xy, callback, scope){
18473         this.callback = callback;
18474         this.scope = scope;
18475         if(xy && this.animRepair !== false){
18476             this.el.addClass("x-dd-drag-repair");
18477             this.el.hideUnders(true);
18478             this.anim = this.el.shift({
18479                 duration: this.repairDuration || .5,
18480                 easing: 'easeOut',
18481                 xy: xy,
18482                 stopFx: true,
18483                 callback: this.afterRepair,
18484                 scope: this
18485             });
18486         }else{
18487             this.afterRepair();
18488         }
18489     },
18490
18491     // private
18492     afterRepair : function(){
18493         this.hide(true);
18494         if(typeof this.callback == "function"){
18495             this.callback.call(this.scope || this);
18496         }
18497         this.callback = null;
18498         this.scope = null;
18499     }
18500 };/*
18501  * Based on:
18502  * Ext JS Library 1.1.1
18503  * Copyright(c) 2006-2007, Ext JS, LLC.
18504  *
18505  * Originally Released Under LGPL - original licence link has changed is not relivant.
18506  *
18507  * Fork - LGPL
18508  * <script type="text/javascript">
18509  */
18510
18511 /**
18512  * @class Roo.dd.DragSource
18513  * @extends Roo.dd.DDProxy
18514  * A simple class that provides the basic implementation needed to make any element draggable.
18515  * @constructor
18516  * @param {String/HTMLElement/Element} el The container element
18517  * @param {Object} config
18518  */
18519 Roo.dd.DragSource = function(el, config){
18520     this.el = Roo.get(el);
18521     this.dragData = {};
18522     
18523     Roo.apply(this, config);
18524     
18525     if(!this.proxy){
18526         this.proxy = new Roo.dd.StatusProxy();
18527     }
18528
18529     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18530           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18531     
18532     this.dragging = false;
18533 };
18534
18535 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18536     /**
18537      * @cfg {String} dropAllowed
18538      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18539      */
18540     dropAllowed : "x-dd-drop-ok",
18541     /**
18542      * @cfg {String} dropNotAllowed
18543      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18544      */
18545     dropNotAllowed : "x-dd-drop-nodrop",
18546
18547     /**
18548      * Returns the data object associated with this drag source
18549      * @return {Object} data An object containing arbitrary data
18550      */
18551     getDragData : function(e){
18552         return this.dragData;
18553     },
18554
18555     // private
18556     onDragEnter : function(e, id){
18557         var target = Roo.dd.DragDropMgr.getDDById(id);
18558         this.cachedTarget = target;
18559         if(this.beforeDragEnter(target, e, id) !== false){
18560             if(target.isNotifyTarget){
18561                 var status = target.notifyEnter(this, e, this.dragData);
18562                 this.proxy.setStatus(status);
18563             }else{
18564                 this.proxy.setStatus(this.dropAllowed);
18565             }
18566             
18567             if(this.afterDragEnter){
18568                 /**
18569                  * An empty function by default, but provided so that you can perform a custom action
18570                  * when the dragged item enters the drop target by providing an implementation.
18571                  * @param {Roo.dd.DragDrop} target The drop target
18572                  * @param {Event} e The event object
18573                  * @param {String} id The id of the dragged element
18574                  * @method afterDragEnter
18575                  */
18576                 this.afterDragEnter(target, e, id);
18577             }
18578         }
18579     },
18580
18581     /**
18582      * An empty function by default, but provided so that you can perform a custom action
18583      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18584      * @param {Roo.dd.DragDrop} target The drop target
18585      * @param {Event} e The event object
18586      * @param {String} id The id of the dragged element
18587      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18588      */
18589     beforeDragEnter : function(target, e, id){
18590         return true;
18591     },
18592
18593     // private
18594     alignElWithMouse: function() {
18595         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18596         this.proxy.sync();
18597     },
18598
18599     // private
18600     onDragOver : function(e, id){
18601         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18602         if(this.beforeDragOver(target, e, id) !== false){
18603             if(target.isNotifyTarget){
18604                 var status = target.notifyOver(this, e, this.dragData);
18605                 this.proxy.setStatus(status);
18606             }
18607
18608             if(this.afterDragOver){
18609                 /**
18610                  * An empty function by default, but provided so that you can perform a custom action
18611                  * while the dragged item is over the drop target by providing an implementation.
18612                  * @param {Roo.dd.DragDrop} target The drop target
18613                  * @param {Event} e The event object
18614                  * @param {String} id The id of the dragged element
18615                  * @method afterDragOver
18616                  */
18617                 this.afterDragOver(target, e, id);
18618             }
18619         }
18620     },
18621
18622     /**
18623      * An empty function by default, but provided so that you can perform a custom action
18624      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18625      * @param {Roo.dd.DragDrop} target The drop target
18626      * @param {Event} e The event object
18627      * @param {String} id The id of the dragged element
18628      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18629      */
18630     beforeDragOver : function(target, e, id){
18631         return true;
18632     },
18633
18634     // private
18635     onDragOut : function(e, id){
18636         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18637         if(this.beforeDragOut(target, e, id) !== false){
18638             if(target.isNotifyTarget){
18639                 target.notifyOut(this, e, this.dragData);
18640             }
18641             this.proxy.reset();
18642             if(this.afterDragOut){
18643                 /**
18644                  * An empty function by default, but provided so that you can perform a custom action
18645                  * after the dragged item is dragged out of the target without dropping.
18646                  * @param {Roo.dd.DragDrop} target The drop target
18647                  * @param {Event} e The event object
18648                  * @param {String} id The id of the dragged element
18649                  * @method afterDragOut
18650                  */
18651                 this.afterDragOut(target, e, id);
18652             }
18653         }
18654         this.cachedTarget = null;
18655     },
18656
18657     /**
18658      * An empty function by default, but provided so that you can perform a custom action before the dragged
18659      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18660      * @param {Roo.dd.DragDrop} target The drop target
18661      * @param {Event} e The event object
18662      * @param {String} id The id of the dragged element
18663      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18664      */
18665     beforeDragOut : function(target, e, id){
18666         return true;
18667     },
18668     
18669     // private
18670     onDragDrop : function(e, id){
18671         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18672         if(this.beforeDragDrop(target, e, id) !== false){
18673             if(target.isNotifyTarget){
18674                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18675                     this.onValidDrop(target, e, id);
18676                 }else{
18677                     this.onInvalidDrop(target, e, id);
18678                 }
18679             }else{
18680                 this.onValidDrop(target, e, id);
18681             }
18682             
18683             if(this.afterDragDrop){
18684                 /**
18685                  * An empty function by default, but provided so that you can perform a custom action
18686                  * after a valid drag drop has occurred by providing an implementation.
18687                  * @param {Roo.dd.DragDrop} target The drop target
18688                  * @param {Event} e The event object
18689                  * @param {String} id The id of the dropped element
18690                  * @method afterDragDrop
18691                  */
18692                 this.afterDragDrop(target, e, id);
18693             }
18694         }
18695         delete this.cachedTarget;
18696     },
18697
18698     /**
18699      * An empty function by default, but provided so that you can perform a custom action before the dragged
18700      * item is dropped onto the target and optionally cancel the onDragDrop.
18701      * @param {Roo.dd.DragDrop} target The drop target
18702      * @param {Event} e The event object
18703      * @param {String} id The id of the dragged element
18704      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18705      */
18706     beforeDragDrop : function(target, e, id){
18707         return true;
18708     },
18709
18710     // private
18711     onValidDrop : function(target, e, id){
18712         this.hideProxy();
18713         if(this.afterValidDrop){
18714             /**
18715              * An empty function by default, but provided so that you can perform a custom action
18716              * after a valid drop has occurred by providing an implementation.
18717              * @param {Object} target The target DD 
18718              * @param {Event} e The event object
18719              * @param {String} id The id of the dropped element
18720              * @method afterInvalidDrop
18721              */
18722             this.afterValidDrop(target, e, id);
18723         }
18724     },
18725
18726     // private
18727     getRepairXY : function(e, data){
18728         return this.el.getXY();  
18729     },
18730
18731     // private
18732     onInvalidDrop : function(target, e, id){
18733         this.beforeInvalidDrop(target, e, id);
18734         if(this.cachedTarget){
18735             if(this.cachedTarget.isNotifyTarget){
18736                 this.cachedTarget.notifyOut(this, e, this.dragData);
18737             }
18738             this.cacheTarget = null;
18739         }
18740         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18741
18742         if(this.afterInvalidDrop){
18743             /**
18744              * An empty function by default, but provided so that you can perform a custom action
18745              * after an invalid drop has occurred by providing an implementation.
18746              * @param {Event} e The event object
18747              * @param {String} id The id of the dropped element
18748              * @method afterInvalidDrop
18749              */
18750             this.afterInvalidDrop(e, id);
18751         }
18752     },
18753
18754     // private
18755     afterRepair : function(){
18756         if(Roo.enableFx){
18757             this.el.highlight(this.hlColor || "c3daf9");
18758         }
18759         this.dragging = false;
18760     },
18761
18762     /**
18763      * An empty function by default, but provided so that you can perform a custom action after an invalid
18764      * drop has occurred.
18765      * @param {Roo.dd.DragDrop} target The drop target
18766      * @param {Event} e The event object
18767      * @param {String} id The id of the dragged element
18768      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18769      */
18770     beforeInvalidDrop : function(target, e, id){
18771         return true;
18772     },
18773
18774     // private
18775     handleMouseDown : function(e){
18776         if(this.dragging) {
18777             return;
18778         }
18779         var data = this.getDragData(e);
18780         if(data && this.onBeforeDrag(data, e) !== false){
18781             this.dragData = data;
18782             this.proxy.stop();
18783             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18784         } 
18785     },
18786
18787     /**
18788      * An empty function by default, but provided so that you can perform a custom action before the initial
18789      * drag event begins and optionally cancel it.
18790      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18791      * @param {Event} e The event object
18792      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18793      */
18794     onBeforeDrag : function(data, e){
18795         return true;
18796     },
18797
18798     /**
18799      * An empty function by default, but provided so that you can perform a custom action once the initial
18800      * drag event has begun.  The drag cannot be canceled from this function.
18801      * @param {Number} x The x position of the click on the dragged object
18802      * @param {Number} y The y position of the click on the dragged object
18803      */
18804     onStartDrag : Roo.emptyFn,
18805
18806     // private - YUI override
18807     startDrag : function(x, y){
18808         this.proxy.reset();
18809         this.dragging = true;
18810         this.proxy.update("");
18811         this.onInitDrag(x, y);
18812         this.proxy.show();
18813     },
18814
18815     // private
18816     onInitDrag : function(x, y){
18817         var clone = this.el.dom.cloneNode(true);
18818         clone.id = Roo.id(); // prevent duplicate ids
18819         this.proxy.update(clone);
18820         this.onStartDrag(x, y);
18821         return true;
18822     },
18823
18824     /**
18825      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18826      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18827      */
18828     getProxy : function(){
18829         return this.proxy;  
18830     },
18831
18832     /**
18833      * Hides the drag source's {@link Roo.dd.StatusProxy}
18834      */
18835     hideProxy : function(){
18836         this.proxy.hide();  
18837         this.proxy.reset(true);
18838         this.dragging = false;
18839     },
18840
18841     // private
18842     triggerCacheRefresh : function(){
18843         Roo.dd.DDM.refreshCache(this.groups);
18844     },
18845
18846     // private - override to prevent hiding
18847     b4EndDrag: function(e) {
18848     },
18849
18850     // private - override to prevent moving
18851     endDrag : function(e){
18852         this.onEndDrag(this.dragData, e);
18853     },
18854
18855     // private
18856     onEndDrag : function(data, e){
18857     },
18858     
18859     // private - pin to cursor
18860     autoOffset : function(x, y) {
18861         this.setDelta(-12, -20);
18862     }    
18863 });/*
18864  * Based on:
18865  * Ext JS Library 1.1.1
18866  * Copyright(c) 2006-2007, Ext JS, LLC.
18867  *
18868  * Originally Released Under LGPL - original licence link has changed is not relivant.
18869  *
18870  * Fork - LGPL
18871  * <script type="text/javascript">
18872  */
18873
18874
18875 /**
18876  * @class Roo.dd.DropTarget
18877  * @extends Roo.dd.DDTarget
18878  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18879  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18880  * @constructor
18881  * @param {String/HTMLElement/Element} el The container element
18882  * @param {Object} config
18883  */
18884 Roo.dd.DropTarget = function(el, config){
18885     this.el = Roo.get(el);
18886     
18887     var listeners = false; ;
18888     if (config && config.listeners) {
18889         listeners= config.listeners;
18890         delete config.listeners;
18891     }
18892     Roo.apply(this, config);
18893     
18894     if(this.containerScroll){
18895         Roo.dd.ScrollManager.register(this.el);
18896     }
18897     this.addEvents( {
18898          /**
18899          * @scope Roo.dd.DropTarget
18900          */
18901          
18902          /**
18903          * @event enter
18904          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18905          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18906          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18907          * 
18908          * IMPORTANT : it should set this.overClass and this.dropAllowed
18909          * 
18910          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18911          * @param {Event} e The event
18912          * @param {Object} data An object containing arbitrary data supplied by the drag source
18913          */
18914         "enter" : true,
18915         
18916          /**
18917          * @event over
18918          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18919          * This method will be called on every mouse movement while the drag source is over the drop target.
18920          * This default implementation simply returns the dropAllowed config value.
18921          * 
18922          * IMPORTANT : it should set this.dropAllowed
18923          * 
18924          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18925          * @param {Event} e The event
18926          * @param {Object} data An object containing arbitrary data supplied by the drag source
18927          
18928          */
18929         "over" : true,
18930         /**
18931          * @event out
18932          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18933          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18934          * overClass (if any) from the drop element.
18935          * 
18936          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18937          * @param {Event} e The event
18938          * @param {Object} data An object containing arbitrary data supplied by the drag source
18939          */
18940          "out" : true,
18941          
18942         /**
18943          * @event drop
18944          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18945          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18946          * implementation that does something to process the drop event and returns true so that the drag source's
18947          * repair action does not run.
18948          * 
18949          * IMPORTANT : it should set this.success
18950          * 
18951          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18952          * @param {Event} e The event
18953          * @param {Object} data An object containing arbitrary data supplied by the drag source
18954         */
18955          "drop" : true
18956     });
18957             
18958      
18959     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18960         this.el.dom, 
18961         this.ddGroup || this.group,
18962         {
18963             isTarget: true,
18964             listeners : listeners || {} 
18965            
18966         
18967         }
18968     );
18969
18970 };
18971
18972 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18973     /**
18974      * @cfg {String} overClass
18975      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18976      */
18977      /**
18978      * @cfg {String} ddGroup
18979      * The drag drop group to handle drop events for
18980      */
18981      
18982     /**
18983      * @cfg {String} dropAllowed
18984      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18985      */
18986     dropAllowed : "x-dd-drop-ok",
18987     /**
18988      * @cfg {String} dropNotAllowed
18989      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18990      */
18991     dropNotAllowed : "x-dd-drop-nodrop",
18992     /**
18993      * @cfg {boolean} success
18994      * set this after drop listener.. 
18995      */
18996     success : false,
18997     /**
18998      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18999      * if the drop point is valid for over/enter..
19000      */
19001     valid : false,
19002     // private
19003     isTarget : true,
19004
19005     // private
19006     isNotifyTarget : true,
19007     
19008     /**
19009      * @hide
19010      */
19011     notifyEnter : function(dd, e, data)
19012     {
19013         this.valid = true;
19014         this.fireEvent('enter', dd, e, data);
19015         if(this.overClass){
19016             this.el.addClass(this.overClass);
19017         }
19018         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
19019             this.valid ? this.dropAllowed : this.dropNotAllowed
19020         );
19021     },
19022
19023     /**
19024      * @hide
19025      */
19026     notifyOver : function(dd, e, data)
19027     {
19028         this.valid = true;
19029         this.fireEvent('over', dd, e, data);
19030         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
19031             this.valid ? this.dropAllowed : this.dropNotAllowed
19032         );
19033     },
19034
19035     /**
19036      * @hide
19037      */
19038     notifyOut : function(dd, e, data)
19039     {
19040         this.fireEvent('out', dd, e, data);
19041         if(this.overClass){
19042             this.el.removeClass(this.overClass);
19043         }
19044     },
19045
19046     /**
19047      * @hide
19048      */
19049     notifyDrop : function(dd, e, data)
19050     {
19051         this.success = false;
19052         this.fireEvent('drop', dd, e, data);
19053         return this.success;
19054     }
19055 });/*
19056  * Based on:
19057  * Ext JS Library 1.1.1
19058  * Copyright(c) 2006-2007, Ext JS, LLC.
19059  *
19060  * Originally Released Under LGPL - original licence link has changed is not relivant.
19061  *
19062  * Fork - LGPL
19063  * <script type="text/javascript">
19064  */
19065
19066
19067 /**
19068  * @class Roo.dd.DragZone
19069  * @extends Roo.dd.DragSource
19070  * This class provides a container DD instance that proxies for multiple child node sources.<br />
19071  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
19072  * @constructor
19073  * @param {String/HTMLElement/Element} el The container element
19074  * @param {Object} config
19075  */
19076 Roo.dd.DragZone = function(el, config){
19077     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
19078     if(this.containerScroll){
19079         Roo.dd.ScrollManager.register(this.el);
19080     }
19081 };
19082
19083 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
19084     /**
19085      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
19086      * for auto scrolling during drag operations.
19087      */
19088     /**
19089      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
19090      * method after a failed drop (defaults to "c3daf9" - light blue)
19091      */
19092
19093     /**
19094      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
19095      * for a valid target to drag based on the mouse down. Override this method
19096      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
19097      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
19098      * @param {EventObject} e The mouse down event
19099      * @return {Object} The dragData
19100      */
19101     getDragData : function(e){
19102         return Roo.dd.Registry.getHandleFromEvent(e);
19103     },
19104     
19105     /**
19106      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
19107      * this.dragData.ddel
19108      * @param {Number} x The x position of the click on the dragged object
19109      * @param {Number} y The y position of the click on the dragged object
19110      * @return {Boolean} true to continue the drag, false to cancel
19111      */
19112     onInitDrag : function(x, y){
19113         this.proxy.update(this.dragData.ddel.cloneNode(true));
19114         this.onStartDrag(x, y);
19115         return true;
19116     },
19117     
19118     /**
19119      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
19120      */
19121     afterRepair : function(){
19122         if(Roo.enableFx){
19123             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
19124         }
19125         this.dragging = false;
19126     },
19127
19128     /**
19129      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
19130      * the XY of this.dragData.ddel
19131      * @param {EventObject} e The mouse up event
19132      * @return {Array} The xy location (e.g. [100, 200])
19133      */
19134     getRepairXY : function(e){
19135         return Roo.Element.fly(this.dragData.ddel).getXY();  
19136     }
19137 });/*
19138  * Based on:
19139  * Ext JS Library 1.1.1
19140  * Copyright(c) 2006-2007, Ext JS, LLC.
19141  *
19142  * Originally Released Under LGPL - original licence link has changed is not relivant.
19143  *
19144  * Fork - LGPL
19145  * <script type="text/javascript">
19146  */
19147 /**
19148  * @class Roo.dd.DropZone
19149  * @extends Roo.dd.DropTarget
19150  * This class provides a container DD instance that proxies for multiple child node targets.<br />
19151  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
19152  * @constructor
19153  * @param {String/HTMLElement/Element} el The container element
19154  * @param {Object} config
19155  */
19156 Roo.dd.DropZone = function(el, config){
19157     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
19158 };
19159
19160 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
19161     /**
19162      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
19163      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
19164      * provide your own custom lookup.
19165      * @param {Event} e The event
19166      * @return {Object} data The custom data
19167      */
19168     getTargetFromEvent : function(e){
19169         return Roo.dd.Registry.getTargetFromEvent(e);
19170     },
19171
19172     /**
19173      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
19174      * that it has registered.  This method has no default implementation and should be overridden to provide
19175      * node-specific processing if necessary.
19176      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
19177      * {@link #getTargetFromEvent} for this node)
19178      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19179      * @param {Event} e The event
19180      * @param {Object} data An object containing arbitrary data supplied by the drag source
19181      */
19182     onNodeEnter : function(n, dd, e, data){
19183         
19184     },
19185
19186     /**
19187      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
19188      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
19189      * overridden to provide the proper feedback.
19190      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19191      * {@link #getTargetFromEvent} for this node)
19192      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19193      * @param {Event} e The event
19194      * @param {Object} data An object containing arbitrary data supplied by the drag source
19195      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19196      * underlying {@link Roo.dd.StatusProxy} can be updated
19197      */
19198     onNodeOver : function(n, dd, e, data){
19199         return this.dropAllowed;
19200     },
19201
19202     /**
19203      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
19204      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
19205      * node-specific processing if necessary.
19206      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19207      * {@link #getTargetFromEvent} for this node)
19208      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19209      * @param {Event} e The event
19210      * @param {Object} data An object containing arbitrary data supplied by the drag source
19211      */
19212     onNodeOut : function(n, dd, e, data){
19213         
19214     },
19215
19216     /**
19217      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
19218      * the drop node.  The default implementation returns false, so it should be overridden to provide the
19219      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
19220      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19221      * {@link #getTargetFromEvent} for this node)
19222      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19223      * @param {Event} e The event
19224      * @param {Object} data An object containing arbitrary data supplied by the drag source
19225      * @return {Boolean} True if the drop was valid, else false
19226      */
19227     onNodeDrop : function(n, dd, e, data){
19228         return false;
19229     },
19230
19231     /**
19232      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
19233      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
19234      * it should be overridden to provide the proper feedback if necessary.
19235      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19236      * @param {Event} e The event
19237      * @param {Object} data An object containing arbitrary data supplied by the drag source
19238      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19239      * underlying {@link Roo.dd.StatusProxy} can be updated
19240      */
19241     onContainerOver : function(dd, e, data){
19242         return this.dropNotAllowed;
19243     },
19244
19245     /**
19246      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
19247      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
19248      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
19249      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
19250      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19251      * @param {Event} e The event
19252      * @param {Object} data An object containing arbitrary data supplied by the drag source
19253      * @return {Boolean} True if the drop was valid, else false
19254      */
19255     onContainerDrop : function(dd, e, data){
19256         return false;
19257     },
19258
19259     /**
19260      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
19261      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
19262      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
19263      * you should override this method and provide a custom implementation.
19264      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19265      * @param {Event} e The event
19266      * @param {Object} data An object containing arbitrary data supplied by the drag source
19267      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19268      * underlying {@link Roo.dd.StatusProxy} can be updated
19269      */
19270     notifyEnter : function(dd, e, data){
19271         return this.dropNotAllowed;
19272     },
19273
19274     /**
19275      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
19276      * This method will be called on every mouse movement while the drag source is over the drop zone.
19277      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
19278      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
19279      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
19280      * registered node, it will call {@link #onContainerOver}.
19281      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19282      * @param {Event} e The event
19283      * @param {Object} data An object containing arbitrary data supplied by the drag source
19284      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19285      * underlying {@link Roo.dd.StatusProxy} can be updated
19286      */
19287     notifyOver : function(dd, e, data){
19288         var n = this.getTargetFromEvent(e);
19289         if(!n){ // not over valid drop target
19290             if(this.lastOverNode){
19291                 this.onNodeOut(this.lastOverNode, dd, e, data);
19292                 this.lastOverNode = null;
19293             }
19294             return this.onContainerOver(dd, e, data);
19295         }
19296         if(this.lastOverNode != n){
19297             if(this.lastOverNode){
19298                 this.onNodeOut(this.lastOverNode, dd, e, data);
19299             }
19300             this.onNodeEnter(n, dd, e, data);
19301             this.lastOverNode = n;
19302         }
19303         return this.onNodeOver(n, dd, e, data);
19304     },
19305
19306     /**
19307      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
19308      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
19309      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
19310      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
19311      * @param {Event} e The event
19312      * @param {Object} data An object containing arbitrary data supplied by the drag zone
19313      */
19314     notifyOut : function(dd, e, data){
19315         if(this.lastOverNode){
19316             this.onNodeOut(this.lastOverNode, dd, e, data);
19317             this.lastOverNode = null;
19318         }
19319     },
19320
19321     /**
19322      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
19323      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
19324      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
19325      * otherwise it will call {@link #onContainerDrop}.
19326      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19327      * @param {Event} e The event
19328      * @param {Object} data An object containing arbitrary data supplied by the drag source
19329      * @return {Boolean} True if the drop was valid, else false
19330      */
19331     notifyDrop : function(dd, e, data){
19332         if(this.lastOverNode){
19333             this.onNodeOut(this.lastOverNode, dd, e, data);
19334             this.lastOverNode = null;
19335         }
19336         var n = this.getTargetFromEvent(e);
19337         return n ?
19338             this.onNodeDrop(n, dd, e, data) :
19339             this.onContainerDrop(dd, e, data);
19340     },
19341
19342     // private
19343     triggerCacheRefresh : function(){
19344         Roo.dd.DDM.refreshCache(this.groups);
19345     }  
19346 });/*
19347  * Based on:
19348  * Ext JS Library 1.1.1
19349  * Copyright(c) 2006-2007, Ext JS, LLC.
19350  *
19351  * Originally Released Under LGPL - original licence link has changed is not relivant.
19352  *
19353  * Fork - LGPL
19354  * <script type="text/javascript">
19355  */
19356
19357
19358 /**
19359  * @class Roo.data.SortTypes
19360  * @singleton
19361  * Defines the default sorting (casting?) comparison functions used when sorting data.
19362  */
19363 Roo.data.SortTypes = {
19364     /**
19365      * Default sort that does nothing
19366      * @param {Mixed} s The value being converted
19367      * @return {Mixed} The comparison value
19368      */
19369     none : function(s){
19370         return s;
19371     },
19372     
19373     /**
19374      * The regular expression used to strip tags
19375      * @type {RegExp}
19376      * @property
19377      */
19378     stripTagsRE : /<\/?[^>]+>/gi,
19379     
19380     /**
19381      * Strips all HTML tags to sort on text only
19382      * @param {Mixed} s The value being converted
19383      * @return {String} The comparison value
19384      */
19385     asText : function(s){
19386         return String(s).replace(this.stripTagsRE, "");
19387     },
19388     
19389     /**
19390      * Strips all HTML tags to sort on text only - Case insensitive
19391      * @param {Mixed} s The value being converted
19392      * @return {String} The comparison value
19393      */
19394     asUCText : function(s){
19395         return String(s).toUpperCase().replace(this.stripTagsRE, "");
19396     },
19397     
19398     /**
19399      * Case insensitive string
19400      * @param {Mixed} s The value being converted
19401      * @return {String} The comparison value
19402      */
19403     asUCString : function(s) {
19404         return String(s).toUpperCase();
19405     },
19406     
19407     /**
19408      * Date sorting
19409      * @param {Mixed} s The value being converted
19410      * @return {Number} The comparison value
19411      */
19412     asDate : function(s) {
19413         if(!s){
19414             return 0;
19415         }
19416         if(s instanceof Date){
19417             return s.getTime();
19418         }
19419         return Date.parse(String(s));
19420     },
19421     
19422     /**
19423      * Float sorting
19424      * @param {Mixed} s The value being converted
19425      * @return {Float} The comparison value
19426      */
19427     asFloat : function(s) {
19428         var val = parseFloat(String(s).replace(/,/g, ""));
19429         if(isNaN(val)) val = 0;
19430         return val;
19431     },
19432     
19433     /**
19434      * Integer sorting
19435      * @param {Mixed} s The value being converted
19436      * @return {Number} The comparison value
19437      */
19438     asInt : function(s) {
19439         var val = parseInt(String(s).replace(/,/g, ""));
19440         if(isNaN(val)) val = 0;
19441         return val;
19442     }
19443 };/*
19444  * Based on:
19445  * Ext JS Library 1.1.1
19446  * Copyright(c) 2006-2007, Ext JS, LLC.
19447  *
19448  * Originally Released Under LGPL - original licence link has changed is not relivant.
19449  *
19450  * Fork - LGPL
19451  * <script type="text/javascript">
19452  */
19453
19454 /**
19455 * @class Roo.data.Record
19456  * Instances of this class encapsulate both record <em>definition</em> information, and record
19457  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
19458  * to access Records cached in an {@link Roo.data.Store} object.<br>
19459  * <p>
19460  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
19461  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
19462  * objects.<br>
19463  * <p>
19464  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
19465  * @constructor
19466  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
19467  * {@link #create}. The parameters are the same.
19468  * @param {Array} data An associative Array of data values keyed by the field name.
19469  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
19470  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
19471  * not specified an integer id is generated.
19472  */
19473 Roo.data.Record = function(data, id){
19474     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
19475     this.data = data;
19476 };
19477
19478 /**
19479  * Generate a constructor for a specific record layout.
19480  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
19481  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
19482  * Each field definition object may contain the following properties: <ul>
19483  * <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,
19484  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
19485  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
19486  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
19487  * is being used, then this is a string containing the javascript expression to reference the data relative to 
19488  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
19489  * to the data item relative to the record element. If the mapping expression is the same as the field name,
19490  * this may be omitted.</p></li>
19491  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
19492  * <ul><li>auto (Default, implies no conversion)</li>
19493  * <li>string</li>
19494  * <li>int</li>
19495  * <li>float</li>
19496  * <li>boolean</li>
19497  * <li>date</li></ul></p></li>
19498  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
19499  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19500  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19501  * by the Reader into an object that will be stored in the Record. It is passed the
19502  * following parameters:<ul>
19503  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19504  * </ul></p></li>
19505  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19506  * </ul>
19507  * <br>usage:<br><pre><code>
19508 var TopicRecord = Roo.data.Record.create(
19509     {name: 'title', mapping: 'topic_title'},
19510     {name: 'author', mapping: 'username'},
19511     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19512     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19513     {name: 'lastPoster', mapping: 'user2'},
19514     {name: 'excerpt', mapping: 'post_text'}
19515 );
19516
19517 var myNewRecord = new TopicRecord({
19518     title: 'Do my job please',
19519     author: 'noobie',
19520     totalPosts: 1,
19521     lastPost: new Date(),
19522     lastPoster: 'Animal',
19523     excerpt: 'No way dude!'
19524 });
19525 myStore.add(myNewRecord);
19526 </code></pre>
19527  * @method create
19528  * @static
19529  */
19530 Roo.data.Record.create = function(o){
19531     var f = function(){
19532         f.superclass.constructor.apply(this, arguments);
19533     };
19534     Roo.extend(f, Roo.data.Record);
19535     var p = f.prototype;
19536     p.fields = new Roo.util.MixedCollection(false, function(field){
19537         return field.name;
19538     });
19539     for(var i = 0, len = o.length; i < len; i++){
19540         p.fields.add(new Roo.data.Field(o[i]));
19541     }
19542     f.getField = function(name){
19543         return p.fields.get(name);  
19544     };
19545     return f;
19546 };
19547
19548 Roo.data.Record.AUTO_ID = 1000;
19549 Roo.data.Record.EDIT = 'edit';
19550 Roo.data.Record.REJECT = 'reject';
19551 Roo.data.Record.COMMIT = 'commit';
19552
19553 Roo.data.Record.prototype = {
19554     /**
19555      * Readonly flag - true if this record has been modified.
19556      * @type Boolean
19557      */
19558     dirty : false,
19559     editing : false,
19560     error: null,
19561     modified: null,
19562
19563     // private
19564     join : function(store){
19565         this.store = store;
19566     },
19567
19568     /**
19569      * Set the named field to the specified value.
19570      * @param {String} name The name of the field to set.
19571      * @param {Object} value The value to set the field to.
19572      */
19573     set : function(name, value){
19574         if(this.data[name] == value){
19575             return;
19576         }
19577         this.dirty = true;
19578         if(!this.modified){
19579             this.modified = {};
19580         }
19581         if(typeof this.modified[name] == 'undefined'){
19582             this.modified[name] = this.data[name];
19583         }
19584         this.data[name] = value;
19585         if(!this.editing && this.store){
19586             this.store.afterEdit(this);
19587         }       
19588     },
19589
19590     /**
19591      * Get the value of the named field.
19592      * @param {String} name The name of the field to get the value of.
19593      * @return {Object} The value of the field.
19594      */
19595     get : function(name){
19596         return this.data[name]; 
19597     },
19598
19599     // private
19600     beginEdit : function(){
19601         this.editing = true;
19602         this.modified = {}; 
19603     },
19604
19605     // private
19606     cancelEdit : function(){
19607         this.editing = false;
19608         delete this.modified;
19609     },
19610
19611     // private
19612     endEdit : function(){
19613         this.editing = false;
19614         if(this.dirty && this.store){
19615             this.store.afterEdit(this);
19616         }
19617     },
19618
19619     /**
19620      * Usually called by the {@link Roo.data.Store} which owns the Record.
19621      * Rejects all changes made to the Record since either creation, or the last commit operation.
19622      * Modified fields are reverted to their original values.
19623      * <p>
19624      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19625      * of reject operations.
19626      */
19627     reject : function(){
19628         var m = this.modified;
19629         for(var n in m){
19630             if(typeof m[n] != "function"){
19631                 this.data[n] = m[n];
19632             }
19633         }
19634         this.dirty = false;
19635         delete this.modified;
19636         this.editing = false;
19637         if(this.store){
19638             this.store.afterReject(this);
19639         }
19640     },
19641
19642     /**
19643      * Usually called by the {@link Roo.data.Store} which owns the Record.
19644      * Commits all changes made to the Record since either creation, or the last commit operation.
19645      * <p>
19646      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19647      * of commit operations.
19648      */
19649     commit : function(){
19650         this.dirty = false;
19651         delete this.modified;
19652         this.editing = false;
19653         if(this.store){
19654             this.store.afterCommit(this);
19655         }
19656     },
19657
19658     // private
19659     hasError : function(){
19660         return this.error != null;
19661     },
19662
19663     // private
19664     clearError : function(){
19665         this.error = null;
19666     },
19667
19668     /**
19669      * Creates a copy of this record.
19670      * @param {String} id (optional) A new record id if you don't want to use this record's id
19671      * @return {Record}
19672      */
19673     copy : function(newId) {
19674         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19675     }
19676 };/*
19677  * Based on:
19678  * Ext JS Library 1.1.1
19679  * Copyright(c) 2006-2007, Ext JS, LLC.
19680  *
19681  * Originally Released Under LGPL - original licence link has changed is not relivant.
19682  *
19683  * Fork - LGPL
19684  * <script type="text/javascript">
19685  */
19686
19687
19688
19689 /**
19690  * @class Roo.data.Store
19691  * @extends Roo.util.Observable
19692  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19693  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19694  * <p>
19695  * 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
19696  * has no knowledge of the format of the data returned by the Proxy.<br>
19697  * <p>
19698  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19699  * instances from the data object. These records are cached and made available through accessor functions.
19700  * @constructor
19701  * Creates a new Store.
19702  * @param {Object} config A config object containing the objects needed for the Store to access data,
19703  * and read the data into Records.
19704  */
19705 Roo.data.Store = function(config){
19706     this.data = new Roo.util.MixedCollection(false);
19707     this.data.getKey = function(o){
19708         return o.id;
19709     };
19710     this.baseParams = {};
19711     // private
19712     this.paramNames = {
19713         "start" : "start",
19714         "limit" : "limit",
19715         "sort" : "sort",
19716         "dir" : "dir",
19717         "multisort" : "_multisort"
19718     };
19719
19720     if(config && config.data){
19721         this.inlineData = config.data;
19722         delete config.data;
19723     }
19724
19725     Roo.apply(this, config);
19726     
19727     if(this.reader){ // reader passed
19728         this.reader = Roo.factory(this.reader, Roo.data);
19729         this.reader.xmodule = this.xmodule || false;
19730         if(!this.recordType){
19731             this.recordType = this.reader.recordType;
19732         }
19733         if(this.reader.onMetaChange){
19734             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19735         }
19736     }
19737
19738     if(this.recordType){
19739         this.fields = this.recordType.prototype.fields;
19740     }
19741     this.modified = [];
19742
19743     this.addEvents({
19744         /**
19745          * @event datachanged
19746          * Fires when the data cache has changed, and a widget which is using this Store
19747          * as a Record cache should refresh its view.
19748          * @param {Store} this
19749          */
19750         datachanged : true,
19751         /**
19752          * @event metachange
19753          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19754          * @param {Store} this
19755          * @param {Object} meta The JSON metadata
19756          */
19757         metachange : true,
19758         /**
19759          * @event add
19760          * Fires when Records have been added to the Store
19761          * @param {Store} this
19762          * @param {Roo.data.Record[]} records The array of Records added
19763          * @param {Number} index The index at which the record(s) were added
19764          */
19765         add : true,
19766         /**
19767          * @event remove
19768          * Fires when a Record has been removed from the Store
19769          * @param {Store} this
19770          * @param {Roo.data.Record} record The Record that was removed
19771          * @param {Number} index The index at which the record was removed
19772          */
19773         remove : true,
19774         /**
19775          * @event update
19776          * Fires when a Record has been updated
19777          * @param {Store} this
19778          * @param {Roo.data.Record} record The Record that was updated
19779          * @param {String} operation The update operation being performed.  Value may be one of:
19780          * <pre><code>
19781  Roo.data.Record.EDIT
19782  Roo.data.Record.REJECT
19783  Roo.data.Record.COMMIT
19784          * </code></pre>
19785          */
19786         update : true,
19787         /**
19788          * @event clear
19789          * Fires when the data cache has been cleared.
19790          * @param {Store} this
19791          */
19792         clear : true,
19793         /**
19794          * @event beforeload
19795          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19796          * the load action will be canceled.
19797          * @param {Store} this
19798          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19799          */
19800         beforeload : true,
19801         /**
19802          * @event load
19803          * Fires after a new set of Records has been loaded.
19804          * @param {Store} this
19805          * @param {Roo.data.Record[]} records The Records that were loaded
19806          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19807          */
19808         load : true,
19809         /**
19810          * @event loadexception
19811          * Fires if an exception occurs in the Proxy during loading.
19812          * Called with the signature of the Proxy's "loadexception" event.
19813          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19814          * 
19815          * @param {Proxy} 
19816          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19817          * @param {Object} load options 
19818          * @param {Object} jsonData from your request (normally this contains the Exception)
19819          */
19820         loadexception : true
19821     });
19822     
19823     if(this.proxy){
19824         this.proxy = Roo.factory(this.proxy, Roo.data);
19825         this.proxy.xmodule = this.xmodule || false;
19826         this.relayEvents(this.proxy,  ["loadexception"]);
19827     }
19828     this.sortToggle = {};
19829     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19830
19831     Roo.data.Store.superclass.constructor.call(this);
19832
19833     if(this.inlineData){
19834         this.loadData(this.inlineData);
19835         delete this.inlineData;
19836     }
19837 };
19838 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19839      /**
19840     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19841     * without a remote query - used by combo/forms at present.
19842     */
19843     
19844     /**
19845     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19846     */
19847     /**
19848     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19849     */
19850     /**
19851     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19852     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19853     */
19854     /**
19855     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19856     * on any HTTP request
19857     */
19858     /**
19859     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19860     */
19861     /**
19862     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19863     */
19864     multiSort: false,
19865     /**
19866     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19867     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19868     */
19869     remoteSort : false,
19870
19871     /**
19872     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19873      * loaded or when a record is removed. (defaults to false).
19874     */
19875     pruneModifiedRecords : false,
19876
19877     // private
19878     lastOptions : null,
19879
19880     /**
19881      * Add Records to the Store and fires the add event.
19882      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19883      */
19884     add : function(records){
19885         records = [].concat(records);
19886         for(var i = 0, len = records.length; i < len; i++){
19887             records[i].join(this);
19888         }
19889         var index = this.data.length;
19890         this.data.addAll(records);
19891         this.fireEvent("add", this, records, index);
19892     },
19893
19894     /**
19895      * Remove a Record from the Store and fires the remove event.
19896      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19897      */
19898     remove : function(record){
19899         var index = this.data.indexOf(record);
19900         this.data.removeAt(index);
19901         if(this.pruneModifiedRecords){
19902             this.modified.remove(record);
19903         }
19904         this.fireEvent("remove", this, record, index);
19905     },
19906
19907     /**
19908      * Remove all Records from the Store and fires the clear event.
19909      */
19910     removeAll : function(){
19911         this.data.clear();
19912         if(this.pruneModifiedRecords){
19913             this.modified = [];
19914         }
19915         this.fireEvent("clear", this);
19916     },
19917
19918     /**
19919      * Inserts Records to the Store at the given index and fires the add event.
19920      * @param {Number} index The start index at which to insert the passed Records.
19921      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19922      */
19923     insert : function(index, records){
19924         records = [].concat(records);
19925         for(var i = 0, len = records.length; i < len; i++){
19926             this.data.insert(index, records[i]);
19927             records[i].join(this);
19928         }
19929         this.fireEvent("add", this, records, index);
19930     },
19931
19932     /**
19933      * Get the index within the cache of the passed Record.
19934      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19935      * @return {Number} The index of the passed Record. Returns -1 if not found.
19936      */
19937     indexOf : function(record){
19938         return this.data.indexOf(record);
19939     },
19940
19941     /**
19942      * Get the index within the cache of the Record with the passed id.
19943      * @param {String} id The id of the Record to find.
19944      * @return {Number} The index of the Record. Returns -1 if not found.
19945      */
19946     indexOfId : function(id){
19947         return this.data.indexOfKey(id);
19948     },
19949
19950     /**
19951      * Get the Record with the specified id.
19952      * @param {String} id The id of the Record to find.
19953      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19954      */
19955     getById : function(id){
19956         return this.data.key(id);
19957     },
19958
19959     /**
19960      * Get the Record at the specified index.
19961      * @param {Number} index The index of the Record to find.
19962      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19963      */
19964     getAt : function(index){
19965         return this.data.itemAt(index);
19966     },
19967
19968     /**
19969      * Returns a range of Records between specified indices.
19970      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19971      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19972      * @return {Roo.data.Record[]} An array of Records
19973      */
19974     getRange : function(start, end){
19975         return this.data.getRange(start, end);
19976     },
19977
19978     // private
19979     storeOptions : function(o){
19980         o = Roo.apply({}, o);
19981         delete o.callback;
19982         delete o.scope;
19983         this.lastOptions = o;
19984     },
19985
19986     /**
19987      * Loads the Record cache from the configured Proxy using the configured Reader.
19988      * <p>
19989      * If using remote paging, then the first load call must specify the <em>start</em>
19990      * and <em>limit</em> properties in the options.params property to establish the initial
19991      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19992      * <p>
19993      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19994      * and this call will return before the new data has been loaded. Perform any post-processing
19995      * in a callback function, or in a "load" event handler.</strong>
19996      * <p>
19997      * @param {Object} options An object containing properties which control loading options:<ul>
19998      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19999      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
20000      * passed the following arguments:<ul>
20001      * <li>r : Roo.data.Record[]</li>
20002      * <li>options: Options object from the load call</li>
20003      * <li>success: Boolean success indicator</li></ul></li>
20004      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
20005      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
20006      * </ul>
20007      */
20008     load : function(options){
20009         options = options || {};
20010         if(this.fireEvent("beforeload", this, options) !== false){
20011             this.storeOptions(options);
20012             var p = Roo.apply(options.params || {}, this.baseParams);
20013             // if meta was not loaded from remote source.. try requesting it.
20014             if (!this.reader.metaFromRemote) {
20015                 p._requestMeta = 1;
20016             }
20017             if(this.sortInfo && this.remoteSort){
20018                 var pn = this.paramNames;
20019                 p[pn["sort"]] = this.sortInfo.field;
20020                 p[pn["dir"]] = this.sortInfo.direction;
20021             }
20022             if (this.multiSort) {
20023                 var pn = this.paramNames;
20024                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
20025             }
20026             
20027             this.proxy.load(p, this.reader, this.loadRecords, this, options);
20028         }
20029     },
20030
20031     /**
20032      * Reloads the Record cache from the configured Proxy using the configured Reader and
20033      * the options from the last load operation performed.
20034      * @param {Object} options (optional) An object containing properties which may override the options
20035      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
20036      * the most recently used options are reused).
20037      */
20038     reload : function(options){
20039         this.load(Roo.applyIf(options||{}, this.lastOptions));
20040     },
20041
20042     // private
20043     // Called as a callback by the Reader during a load operation.
20044     loadRecords : function(o, options, success){
20045         if(!o || success === false){
20046             if(success !== false){
20047                 this.fireEvent("load", this, [], options);
20048             }
20049             if(options.callback){
20050                 options.callback.call(options.scope || this, [], options, false);
20051             }
20052             return;
20053         }
20054         // if data returned failure - throw an exception.
20055         if (o.success === false) {
20056             // show a message if no listener is registered.
20057             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
20058                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
20059             }
20060             // loadmask wil be hooked into this..
20061             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
20062             return;
20063         }
20064         var r = o.records, t = o.totalRecords || r.length;
20065         if(!options || options.add !== true){
20066             if(this.pruneModifiedRecords){
20067                 this.modified = [];
20068             }
20069             for(var i = 0, len = r.length; i < len; i++){
20070                 r[i].join(this);
20071             }
20072             if(this.snapshot){
20073                 this.data = this.snapshot;
20074                 delete this.snapshot;
20075             }
20076             this.data.clear();
20077             this.data.addAll(r);
20078             this.totalLength = t;
20079             this.applySort();
20080             this.fireEvent("datachanged", this);
20081         }else{
20082             this.totalLength = Math.max(t, this.data.length+r.length);
20083             this.add(r);
20084         }
20085         this.fireEvent("load", this, r, options);
20086         if(options.callback){
20087             options.callback.call(options.scope || this, r, options, true);
20088         }
20089     },
20090
20091
20092     /**
20093      * Loads data from a passed data block. A Reader which understands the format of the data
20094      * must have been configured in the constructor.
20095      * @param {Object} data The data block from which to read the Records.  The format of the data expected
20096      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
20097      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
20098      */
20099     loadData : function(o, append){
20100         var r = this.reader.readRecords(o);
20101         this.loadRecords(r, {add: append}, true);
20102     },
20103
20104     /**
20105      * Gets the number of cached records.
20106      * <p>
20107      * <em>If using paging, this may not be the total size of the dataset. If the data object
20108      * used by the Reader contains the dataset size, then the getTotalCount() function returns
20109      * the data set size</em>
20110      */
20111     getCount : function(){
20112         return this.data.length || 0;
20113     },
20114
20115     /**
20116      * Gets the total number of records in the dataset as returned by the server.
20117      * <p>
20118      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
20119      * the dataset size</em>
20120      */
20121     getTotalCount : function(){
20122         return this.totalLength || 0;
20123     },
20124
20125     /**
20126      * Returns the sort state of the Store as an object with two properties:
20127      * <pre><code>
20128  field {String} The name of the field by which the Records are sorted
20129  direction {String} The sort order, "ASC" or "DESC"
20130      * </code></pre>
20131      */
20132     getSortState : function(){
20133         return this.sortInfo;
20134     },
20135
20136     // private
20137     applySort : function(){
20138         if(this.sortInfo && !this.remoteSort){
20139             var s = this.sortInfo, f = s.field;
20140             var st = this.fields.get(f).sortType;
20141             var fn = function(r1, r2){
20142                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
20143                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
20144             };
20145             this.data.sort(s.direction, fn);
20146             if(this.snapshot && this.snapshot != this.data){
20147                 this.snapshot.sort(s.direction, fn);
20148             }
20149         }
20150     },
20151
20152     /**
20153      * Sets the default sort column and order to be used by the next load operation.
20154      * @param {String} fieldName The name of the field to sort by.
20155      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20156      */
20157     setDefaultSort : function(field, dir){
20158         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
20159     },
20160
20161     /**
20162      * Sort the Records.
20163      * If remote sorting is used, the sort is performed on the server, and the cache is
20164      * reloaded. If local sorting is used, the cache is sorted internally.
20165      * @param {String} fieldName The name of the field to sort by.
20166      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20167      */
20168     sort : function(fieldName, dir){
20169         var f = this.fields.get(fieldName);
20170         if(!dir){
20171             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
20172             
20173             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
20174                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
20175             }else{
20176                 dir = f.sortDir;
20177             }
20178         }
20179         this.sortToggle[f.name] = dir;
20180         this.sortInfo = {field: f.name, direction: dir};
20181         if(!this.remoteSort){
20182             this.applySort();
20183             this.fireEvent("datachanged", this);
20184         }else{
20185             this.load(this.lastOptions);
20186         }
20187     },
20188
20189     /**
20190      * Calls the specified function for each of the Records in the cache.
20191      * @param {Function} fn The function to call. The Record is passed as the first parameter.
20192      * Returning <em>false</em> aborts and exits the iteration.
20193      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
20194      */
20195     each : function(fn, scope){
20196         this.data.each(fn, scope);
20197     },
20198
20199     /**
20200      * Gets all records modified since the last commit.  Modified records are persisted across load operations
20201      * (e.g., during paging).
20202      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
20203      */
20204     getModifiedRecords : function(){
20205         return this.modified;
20206     },
20207
20208     // private
20209     createFilterFn : function(property, value, anyMatch){
20210         if(!value.exec){ // not a regex
20211             value = String(value);
20212             if(value.length == 0){
20213                 return false;
20214             }
20215             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
20216         }
20217         return function(r){
20218             return value.test(r.data[property]);
20219         };
20220     },
20221
20222     /**
20223      * Sums the value of <i>property</i> for each record between start and end and returns the result.
20224      * @param {String} property A field on your records
20225      * @param {Number} start The record index to start at (defaults to 0)
20226      * @param {Number} end The last record index to include (defaults to length - 1)
20227      * @return {Number} The sum
20228      */
20229     sum : function(property, start, end){
20230         var rs = this.data.items, v = 0;
20231         start = start || 0;
20232         end = (end || end === 0) ? end : rs.length-1;
20233
20234         for(var i = start; i <= end; i++){
20235             v += (rs[i].data[property] || 0);
20236         }
20237         return v;
20238     },
20239
20240     /**
20241      * Filter the records by a specified property.
20242      * @param {String} field A field on your records
20243      * @param {String/RegExp} value Either a string that the field
20244      * should start with or a RegExp to test against the field
20245      * @param {Boolean} anyMatch True to match any part not just the beginning
20246      */
20247     filter : function(property, value, anyMatch){
20248         var fn = this.createFilterFn(property, value, anyMatch);
20249         return fn ? this.filterBy(fn) : this.clearFilter();
20250     },
20251
20252     /**
20253      * Filter by a function. The specified function will be called with each
20254      * record in this data source. If the function returns true the record is included,
20255      * otherwise it is filtered.
20256      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20257      * @param {Object} scope (optional) The scope of the function (defaults to this)
20258      */
20259     filterBy : function(fn, scope){
20260         this.snapshot = this.snapshot || this.data;
20261         this.data = this.queryBy(fn, scope||this);
20262         this.fireEvent("datachanged", this);
20263     },
20264
20265     /**
20266      * Query the records by a specified property.
20267      * @param {String} field A field on your records
20268      * @param {String/RegExp} value Either a string that the field
20269      * should start with or a RegExp to test against the field
20270      * @param {Boolean} anyMatch True to match any part not just the beginning
20271      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20272      */
20273     query : function(property, value, anyMatch){
20274         var fn = this.createFilterFn(property, value, anyMatch);
20275         return fn ? this.queryBy(fn) : this.data.clone();
20276     },
20277
20278     /**
20279      * Query by a function. The specified function will be called with each
20280      * record in this data source. If the function returns true the record is included
20281      * in the results.
20282      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20283      * @param {Object} scope (optional) The scope of the function (defaults to this)
20284       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20285      **/
20286     queryBy : function(fn, scope){
20287         var data = this.snapshot || this.data;
20288         return data.filterBy(fn, scope||this);
20289     },
20290
20291     /**
20292      * Collects unique values for a particular dataIndex from this store.
20293      * @param {String} dataIndex The property to collect
20294      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
20295      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
20296      * @return {Array} An array of the unique values
20297      **/
20298     collect : function(dataIndex, allowNull, bypassFilter){
20299         var d = (bypassFilter === true && this.snapshot) ?
20300                 this.snapshot.items : this.data.items;
20301         var v, sv, r = [], l = {};
20302         for(var i = 0, len = d.length; i < len; i++){
20303             v = d[i].data[dataIndex];
20304             sv = String(v);
20305             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
20306                 l[sv] = true;
20307                 r[r.length] = v;
20308             }
20309         }
20310         return r;
20311     },
20312
20313     /**
20314      * Revert to a view of the Record cache with no filtering applied.
20315      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
20316      */
20317     clearFilter : function(suppressEvent){
20318         if(this.snapshot && this.snapshot != this.data){
20319             this.data = this.snapshot;
20320             delete this.snapshot;
20321             if(suppressEvent !== true){
20322                 this.fireEvent("datachanged", this);
20323             }
20324         }
20325     },
20326
20327     // private
20328     afterEdit : function(record){
20329         if(this.modified.indexOf(record) == -1){
20330             this.modified.push(record);
20331         }
20332         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
20333     },
20334     
20335     // private
20336     afterReject : function(record){
20337         this.modified.remove(record);
20338         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
20339     },
20340
20341     // private
20342     afterCommit : function(record){
20343         this.modified.remove(record);
20344         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
20345     },
20346
20347     /**
20348      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
20349      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
20350      */
20351     commitChanges : function(){
20352         var m = this.modified.slice(0);
20353         this.modified = [];
20354         for(var i = 0, len = m.length; i < len; i++){
20355             m[i].commit();
20356         }
20357     },
20358
20359     /**
20360      * Cancel outstanding changes on all changed records.
20361      */
20362     rejectChanges : function(){
20363         var m = this.modified.slice(0);
20364         this.modified = [];
20365         for(var i = 0, len = m.length; i < len; i++){
20366             m[i].reject();
20367         }
20368     },
20369
20370     onMetaChange : function(meta, rtype, o){
20371         this.recordType = rtype;
20372         this.fields = rtype.prototype.fields;
20373         delete this.snapshot;
20374         this.sortInfo = meta.sortInfo || this.sortInfo;
20375         this.modified = [];
20376         this.fireEvent('metachange', this, this.reader.meta);
20377     }
20378 });/*
20379  * Based on:
20380  * Ext JS Library 1.1.1
20381  * Copyright(c) 2006-2007, Ext JS, LLC.
20382  *
20383  * Originally Released Under LGPL - original licence link has changed is not relivant.
20384  *
20385  * Fork - LGPL
20386  * <script type="text/javascript">
20387  */
20388
20389 /**
20390  * @class Roo.data.SimpleStore
20391  * @extends Roo.data.Store
20392  * Small helper class to make creating Stores from Array data easier.
20393  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
20394  * @cfg {Array} fields An array of field definition objects, or field name strings.
20395  * @cfg {Array} data The multi-dimensional array of data
20396  * @constructor
20397  * @param {Object} config
20398  */
20399 Roo.data.SimpleStore = function(config){
20400     Roo.data.SimpleStore.superclass.constructor.call(this, {
20401         isLocal : true,
20402         reader: new Roo.data.ArrayReader({
20403                 id: config.id
20404             },
20405             Roo.data.Record.create(config.fields)
20406         ),
20407         proxy : new Roo.data.MemoryProxy(config.data)
20408     });
20409     this.load();
20410 };
20411 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
20412  * Based on:
20413  * Ext JS Library 1.1.1
20414  * Copyright(c) 2006-2007, Ext JS, LLC.
20415  *
20416  * Originally Released Under LGPL - original licence link has changed is not relivant.
20417  *
20418  * Fork - LGPL
20419  * <script type="text/javascript">
20420  */
20421
20422 /**
20423 /**
20424  * @extends Roo.data.Store
20425  * @class Roo.data.JsonStore
20426  * Small helper class to make creating Stores for JSON data easier. <br/>
20427 <pre><code>
20428 var store = new Roo.data.JsonStore({
20429     url: 'get-images.php',
20430     root: 'images',
20431     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
20432 });
20433 </code></pre>
20434  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
20435  * JsonReader and HttpProxy (unless inline data is provided).</b>
20436  * @cfg {Array} fields An array of field definition objects, or field name strings.
20437  * @constructor
20438  * @param {Object} config
20439  */
20440 Roo.data.JsonStore = function(c){
20441     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
20442         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
20443         reader: new Roo.data.JsonReader(c, c.fields)
20444     }));
20445 };
20446 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
20447  * Based on:
20448  * Ext JS Library 1.1.1
20449  * Copyright(c) 2006-2007, Ext JS, LLC.
20450  *
20451  * Originally Released Under LGPL - original licence link has changed is not relivant.
20452  *
20453  * Fork - LGPL
20454  * <script type="text/javascript">
20455  */
20456
20457  
20458 Roo.data.Field = function(config){
20459     if(typeof config == "string"){
20460         config = {name: config};
20461     }
20462     Roo.apply(this, config);
20463     
20464     if(!this.type){
20465         this.type = "auto";
20466     }
20467     
20468     var st = Roo.data.SortTypes;
20469     // named sortTypes are supported, here we look them up
20470     if(typeof this.sortType == "string"){
20471         this.sortType = st[this.sortType];
20472     }
20473     
20474     // set default sortType for strings and dates
20475     if(!this.sortType){
20476         switch(this.type){
20477             case "string":
20478                 this.sortType = st.asUCString;
20479                 break;
20480             case "date":
20481                 this.sortType = st.asDate;
20482                 break;
20483             default:
20484                 this.sortType = st.none;
20485         }
20486     }
20487
20488     // define once
20489     var stripRe = /[\$,%]/g;
20490
20491     // prebuilt conversion function for this field, instead of
20492     // switching every time we're reading a value
20493     if(!this.convert){
20494         var cv, dateFormat = this.dateFormat;
20495         switch(this.type){
20496             case "":
20497             case "auto":
20498             case undefined:
20499                 cv = function(v){ return v; };
20500                 break;
20501             case "string":
20502                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20503                 break;
20504             case "int":
20505                 cv = function(v){
20506                     return v !== undefined && v !== null && v !== '' ?
20507                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20508                     };
20509                 break;
20510             case "float":
20511                 cv = function(v){
20512                     return v !== undefined && v !== null && v !== '' ?
20513                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20514                     };
20515                 break;
20516             case "bool":
20517             case "boolean":
20518                 cv = function(v){ return v === true || v === "true" || v == 1; };
20519                 break;
20520             case "date":
20521                 cv = function(v){
20522                     if(!v){
20523                         return '';
20524                     }
20525                     if(v instanceof Date){
20526                         return v;
20527                     }
20528                     if(dateFormat){
20529                         if(dateFormat == "timestamp"){
20530                             return new Date(v*1000);
20531                         }
20532                         return Date.parseDate(v, dateFormat);
20533                     }
20534                     var parsed = Date.parse(v);
20535                     return parsed ? new Date(parsed) : null;
20536                 };
20537              break;
20538             
20539         }
20540         this.convert = cv;
20541     }
20542 };
20543
20544 Roo.data.Field.prototype = {
20545     dateFormat: null,
20546     defaultValue: "",
20547     mapping: null,
20548     sortType : null,
20549     sortDir : "ASC"
20550 };/*
20551  * Based on:
20552  * Ext JS Library 1.1.1
20553  * Copyright(c) 2006-2007, Ext JS, LLC.
20554  *
20555  * Originally Released Under LGPL - original licence link has changed is not relivant.
20556  *
20557  * Fork - LGPL
20558  * <script type="text/javascript">
20559  */
20560  
20561 // Base class for reading structured data from a data source.  This class is intended to be
20562 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20563
20564 /**
20565  * @class Roo.data.DataReader
20566  * Base class for reading structured data from a data source.  This class is intended to be
20567  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20568  */
20569
20570 Roo.data.DataReader = function(meta, recordType){
20571     
20572     this.meta = meta;
20573     
20574     this.recordType = recordType instanceof Array ? 
20575         Roo.data.Record.create(recordType) : recordType;
20576 };
20577
20578 Roo.data.DataReader.prototype = {
20579      /**
20580      * Create an empty record
20581      * @param {Object} data (optional) - overlay some values
20582      * @return {Roo.data.Record} record created.
20583      */
20584     newRow :  function(d) {
20585         var da =  {};
20586         this.recordType.prototype.fields.each(function(c) {
20587             switch( c.type) {
20588                 case 'int' : da[c.name] = 0; break;
20589                 case 'date' : da[c.name] = new Date(); break;
20590                 case 'float' : da[c.name] = 0.0; break;
20591                 case 'boolean' : da[c.name] = false; break;
20592                 default : da[c.name] = ""; break;
20593             }
20594             
20595         });
20596         return new this.recordType(Roo.apply(da, d));
20597     }
20598     
20599 };/*
20600  * Based on:
20601  * Ext JS Library 1.1.1
20602  * Copyright(c) 2006-2007, Ext JS, LLC.
20603  *
20604  * Originally Released Under LGPL - original licence link has changed is not relivant.
20605  *
20606  * Fork - LGPL
20607  * <script type="text/javascript">
20608  */
20609
20610 /**
20611  * @class Roo.data.DataProxy
20612  * @extends Roo.data.Observable
20613  * This class is an abstract base class for implementations which provide retrieval of
20614  * unformatted data objects.<br>
20615  * <p>
20616  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20617  * (of the appropriate type which knows how to parse the data object) to provide a block of
20618  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20619  * <p>
20620  * Custom implementations must implement the load method as described in
20621  * {@link Roo.data.HttpProxy#load}.
20622  */
20623 Roo.data.DataProxy = function(){
20624     this.addEvents({
20625         /**
20626          * @event beforeload
20627          * Fires before a network request is made to retrieve a data object.
20628          * @param {Object} This DataProxy object.
20629          * @param {Object} params The params parameter to the load function.
20630          */
20631         beforeload : true,
20632         /**
20633          * @event load
20634          * Fires before the load method's callback is called.
20635          * @param {Object} This DataProxy object.
20636          * @param {Object} o The data object.
20637          * @param {Object} arg The callback argument object passed to the load function.
20638          */
20639         load : true,
20640         /**
20641          * @event loadexception
20642          * Fires if an Exception occurs during data retrieval.
20643          * @param {Object} This DataProxy object.
20644          * @param {Object} o The data object.
20645          * @param {Object} arg The callback argument object passed to the load function.
20646          * @param {Object} e The Exception.
20647          */
20648         loadexception : true
20649     });
20650     Roo.data.DataProxy.superclass.constructor.call(this);
20651 };
20652
20653 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20654
20655     /**
20656      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20657      */
20658 /*
20659  * Based on:
20660  * Ext JS Library 1.1.1
20661  * Copyright(c) 2006-2007, Ext JS, LLC.
20662  *
20663  * Originally Released Under LGPL - original licence link has changed is not relivant.
20664  *
20665  * Fork - LGPL
20666  * <script type="text/javascript">
20667  */
20668 /**
20669  * @class Roo.data.MemoryProxy
20670  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20671  * to the Reader when its load method is called.
20672  * @constructor
20673  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20674  */
20675 Roo.data.MemoryProxy = function(data){
20676     if (data.data) {
20677         data = data.data;
20678     }
20679     Roo.data.MemoryProxy.superclass.constructor.call(this);
20680     this.data = data;
20681 };
20682
20683 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20684     /**
20685      * Load data from the requested source (in this case an in-memory
20686      * data object passed to the constructor), read the data object into
20687      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20688      * process that block using the passed callback.
20689      * @param {Object} params This parameter is not used by the MemoryProxy class.
20690      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20691      * object into a block of Roo.data.Records.
20692      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20693      * The function must be passed <ul>
20694      * <li>The Record block object</li>
20695      * <li>The "arg" argument from the load function</li>
20696      * <li>A boolean success indicator</li>
20697      * </ul>
20698      * @param {Object} scope The scope in which to call the callback
20699      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20700      */
20701     load : function(params, reader, callback, scope, arg){
20702         params = params || {};
20703         var result;
20704         try {
20705             result = reader.readRecords(this.data);
20706         }catch(e){
20707             this.fireEvent("loadexception", this, arg, null, e);
20708             callback.call(scope, null, arg, false);
20709             return;
20710         }
20711         callback.call(scope, result, arg, true);
20712     },
20713     
20714     // private
20715     update : function(params, records){
20716         
20717     }
20718 });/*
20719  * Based on:
20720  * Ext JS Library 1.1.1
20721  * Copyright(c) 2006-2007, Ext JS, LLC.
20722  *
20723  * Originally Released Under LGPL - original licence link has changed is not relivant.
20724  *
20725  * Fork - LGPL
20726  * <script type="text/javascript">
20727  */
20728 /**
20729  * @class Roo.data.HttpProxy
20730  * @extends Roo.data.DataProxy
20731  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20732  * configured to reference a certain URL.<br><br>
20733  * <p>
20734  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20735  * from which the running page was served.<br><br>
20736  * <p>
20737  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20738  * <p>
20739  * Be aware that to enable the browser to parse an XML document, the server must set
20740  * the Content-Type header in the HTTP response to "text/xml".
20741  * @constructor
20742  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20743  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20744  * will be used to make the request.
20745  */
20746 Roo.data.HttpProxy = function(conn){
20747     Roo.data.HttpProxy.superclass.constructor.call(this);
20748     // is conn a conn config or a real conn?
20749     this.conn = conn;
20750     this.useAjax = !conn || !conn.events;
20751   
20752 };
20753
20754 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20755     // thse are take from connection...
20756     
20757     /**
20758      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20759      */
20760     /**
20761      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20762      * extra parameters to each request made by this object. (defaults to undefined)
20763      */
20764     /**
20765      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20766      *  to each request made by this object. (defaults to undefined)
20767      */
20768     /**
20769      * @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)
20770      */
20771     /**
20772      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20773      */
20774      /**
20775      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20776      * @type Boolean
20777      */
20778   
20779
20780     /**
20781      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20782      * @type Boolean
20783      */
20784     /**
20785      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20786      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20787      * a finer-grained basis than the DataProxy events.
20788      */
20789     getConnection : function(){
20790         return this.useAjax ? Roo.Ajax : this.conn;
20791     },
20792
20793     /**
20794      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20795      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20796      * process that block using the passed callback.
20797      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20798      * for the request to the remote server.
20799      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20800      * object into a block of Roo.data.Records.
20801      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20802      * The function must be passed <ul>
20803      * <li>The Record block object</li>
20804      * <li>The "arg" argument from the load function</li>
20805      * <li>A boolean success indicator</li>
20806      * </ul>
20807      * @param {Object} scope The scope in which to call the callback
20808      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20809      */
20810     load : function(params, reader, callback, scope, arg){
20811         if(this.fireEvent("beforeload", this, params) !== false){
20812             var  o = {
20813                 params : params || {},
20814                 request: {
20815                     callback : callback,
20816                     scope : scope,
20817                     arg : arg
20818                 },
20819                 reader: reader,
20820                 callback : this.loadResponse,
20821                 scope: this
20822             };
20823             if(this.useAjax){
20824                 Roo.applyIf(o, this.conn);
20825                 if(this.activeRequest){
20826                     Roo.Ajax.abort(this.activeRequest);
20827                 }
20828                 this.activeRequest = Roo.Ajax.request(o);
20829             }else{
20830                 this.conn.request(o);
20831             }
20832         }else{
20833             callback.call(scope||this, null, arg, false);
20834         }
20835     },
20836
20837     // private
20838     loadResponse : function(o, success, response){
20839         delete this.activeRequest;
20840         if(!success){
20841             this.fireEvent("loadexception", this, o, response);
20842             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20843             return;
20844         }
20845         var result;
20846         try {
20847             result = o.reader.read(response);
20848         }catch(e){
20849             this.fireEvent("loadexception", this, o, response, e);
20850             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20851             return;
20852         }
20853         
20854         this.fireEvent("load", this, o, o.request.arg);
20855         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20856     },
20857
20858     // private
20859     update : function(dataSet){
20860
20861     },
20862
20863     // private
20864     updateResponse : function(dataSet){
20865
20866     }
20867 });/*
20868  * Based on:
20869  * Ext JS Library 1.1.1
20870  * Copyright(c) 2006-2007, Ext JS, LLC.
20871  *
20872  * Originally Released Under LGPL - original licence link has changed is not relivant.
20873  *
20874  * Fork - LGPL
20875  * <script type="text/javascript">
20876  */
20877
20878 /**
20879  * @class Roo.data.ScriptTagProxy
20880  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20881  * other than the originating domain of the running page.<br><br>
20882  * <p>
20883  * <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
20884  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20885  * <p>
20886  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20887  * source code that is used as the source inside a &lt;script> tag.<br><br>
20888  * <p>
20889  * In order for the browser to process the returned data, the server must wrap the data object
20890  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20891  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20892  * depending on whether the callback name was passed:
20893  * <p>
20894  * <pre><code>
20895 boolean scriptTag = false;
20896 String cb = request.getParameter("callback");
20897 if (cb != null) {
20898     scriptTag = true;
20899     response.setContentType("text/javascript");
20900 } else {
20901     response.setContentType("application/x-json");
20902 }
20903 Writer out = response.getWriter();
20904 if (scriptTag) {
20905     out.write(cb + "(");
20906 }
20907 out.print(dataBlock.toJsonString());
20908 if (scriptTag) {
20909     out.write(");");
20910 }
20911 </pre></code>
20912  *
20913  * @constructor
20914  * @param {Object} config A configuration object.
20915  */
20916 Roo.data.ScriptTagProxy = function(config){
20917     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20918     Roo.apply(this, config);
20919     this.head = document.getElementsByTagName("head")[0];
20920 };
20921
20922 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20923
20924 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20925     /**
20926      * @cfg {String} url The URL from which to request the data object.
20927      */
20928     /**
20929      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20930      */
20931     timeout : 30000,
20932     /**
20933      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20934      * the server the name of the callback function set up by the load call to process the returned data object.
20935      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20936      * javascript output which calls this named function passing the data object as its only parameter.
20937      */
20938     callbackParam : "callback",
20939     /**
20940      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20941      * name to the request.
20942      */
20943     nocache : true,
20944
20945     /**
20946      * Load data from the configured URL, read the data object into
20947      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20948      * process that block using the passed callback.
20949      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20950      * for the request to the remote server.
20951      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20952      * object into a block of Roo.data.Records.
20953      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20954      * The function must be passed <ul>
20955      * <li>The Record block object</li>
20956      * <li>The "arg" argument from the load function</li>
20957      * <li>A boolean success indicator</li>
20958      * </ul>
20959      * @param {Object} scope The scope in which to call the callback
20960      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20961      */
20962     load : function(params, reader, callback, scope, arg){
20963         if(this.fireEvent("beforeload", this, params) !== false){
20964
20965             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20966
20967             var url = this.url;
20968             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20969             if(this.nocache){
20970                 url += "&_dc=" + (new Date().getTime());
20971             }
20972             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20973             var trans = {
20974                 id : transId,
20975                 cb : "stcCallback"+transId,
20976                 scriptId : "stcScript"+transId,
20977                 params : params,
20978                 arg : arg,
20979                 url : url,
20980                 callback : callback,
20981                 scope : scope,
20982                 reader : reader
20983             };
20984             var conn = this;
20985
20986             window[trans.cb] = function(o){
20987                 conn.handleResponse(o, trans);
20988             };
20989
20990             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20991
20992             if(this.autoAbort !== false){
20993                 this.abort();
20994             }
20995
20996             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20997
20998             var script = document.createElement("script");
20999             script.setAttribute("src", url);
21000             script.setAttribute("type", "text/javascript");
21001             script.setAttribute("id", trans.scriptId);
21002             this.head.appendChild(script);
21003
21004             this.trans = trans;
21005         }else{
21006             callback.call(scope||this, null, arg, false);
21007         }
21008     },
21009
21010     // private
21011     isLoading : function(){
21012         return this.trans ? true : false;
21013     },
21014
21015     /**
21016      * Abort the current server request.
21017      */
21018     abort : function(){
21019         if(this.isLoading()){
21020             this.destroyTrans(this.trans);
21021         }
21022     },
21023
21024     // private
21025     destroyTrans : function(trans, isLoaded){
21026         this.head.removeChild(document.getElementById(trans.scriptId));
21027         clearTimeout(trans.timeoutId);
21028         if(isLoaded){
21029             window[trans.cb] = undefined;
21030             try{
21031                 delete window[trans.cb];
21032             }catch(e){}
21033         }else{
21034             // if hasn't been loaded, wait for load to remove it to prevent script error
21035             window[trans.cb] = function(){
21036                 window[trans.cb] = undefined;
21037                 try{
21038                     delete window[trans.cb];
21039                 }catch(e){}
21040             };
21041         }
21042     },
21043
21044     // private
21045     handleResponse : function(o, trans){
21046         this.trans = false;
21047         this.destroyTrans(trans, true);
21048         var result;
21049         try {
21050             result = trans.reader.readRecords(o);
21051         }catch(e){
21052             this.fireEvent("loadexception", this, o, trans.arg, e);
21053             trans.callback.call(trans.scope||window, null, trans.arg, false);
21054             return;
21055         }
21056         this.fireEvent("load", this, o, trans.arg);
21057         trans.callback.call(trans.scope||window, result, trans.arg, true);
21058     },
21059
21060     // private
21061     handleFailure : function(trans){
21062         this.trans = false;
21063         this.destroyTrans(trans, false);
21064         this.fireEvent("loadexception", this, null, trans.arg);
21065         trans.callback.call(trans.scope||window, null, trans.arg, false);
21066     }
21067 });/*
21068  * Based on:
21069  * Ext JS Library 1.1.1
21070  * Copyright(c) 2006-2007, Ext JS, LLC.
21071  *
21072  * Originally Released Under LGPL - original licence link has changed is not relivant.
21073  *
21074  * Fork - LGPL
21075  * <script type="text/javascript">
21076  */
21077
21078 /**
21079  * @class Roo.data.JsonReader
21080  * @extends Roo.data.DataReader
21081  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
21082  * based on mappings in a provided Roo.data.Record constructor.
21083  * 
21084  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
21085  * in the reply previously. 
21086  * 
21087  * <p>
21088  * Example code:
21089  * <pre><code>
21090 var RecordDef = Roo.data.Record.create([
21091     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21092     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21093 ]);
21094 var myReader = new Roo.data.JsonReader({
21095     totalProperty: "results",    // The property which contains the total dataset size (optional)
21096     root: "rows",                // The property which contains an Array of row objects
21097     id: "id"                     // The property within each row object that provides an ID for the record (optional)
21098 }, RecordDef);
21099 </code></pre>
21100  * <p>
21101  * This would consume a JSON file like this:
21102  * <pre><code>
21103 { 'results': 2, 'rows': [
21104     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
21105     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
21106 }
21107 </code></pre>
21108  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
21109  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21110  * paged from the remote server.
21111  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
21112  * @cfg {String} root name of the property which contains the Array of row objects.
21113  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
21114  * @constructor
21115  * Create a new JsonReader
21116  * @param {Object} meta Metadata configuration options
21117  * @param {Object} recordType Either an Array of field definition objects,
21118  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
21119  */
21120 Roo.data.JsonReader = function(meta, recordType){
21121     
21122     meta = meta || {};
21123     // set some defaults:
21124     Roo.applyIf(meta, {
21125         totalProperty: 'total',
21126         successProperty : 'success',
21127         root : 'data',
21128         id : 'id'
21129     });
21130     
21131     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21132 };
21133 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
21134     
21135     /**
21136      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
21137      * Used by Store query builder to append _requestMeta to params.
21138      * 
21139      */
21140     metaFromRemote : false,
21141     /**
21142      * This method is only used by a DataProxy which has retrieved data from a remote server.
21143      * @param {Object} response The XHR object which contains the JSON data in its responseText.
21144      * @return {Object} data A data block which is used by an Roo.data.Store object as
21145      * a cache of Roo.data.Records.
21146      */
21147     read : function(response){
21148         var json = response.responseText;
21149        
21150         var o = /* eval:var:o */ eval("("+json+")");
21151         if(!o) {
21152             throw {message: "JsonReader.read: Json object not found"};
21153         }
21154         
21155         if(o.metaData){
21156             
21157             delete this.ef;
21158             this.metaFromRemote = true;
21159             this.meta = o.metaData;
21160             this.recordType = Roo.data.Record.create(o.metaData.fields);
21161             this.onMetaChange(this.meta, this.recordType, o);
21162         }
21163         return this.readRecords(o);
21164     },
21165
21166     // private function a store will implement
21167     onMetaChange : function(meta, recordType, o){
21168
21169     },
21170
21171     /**
21172          * @ignore
21173          */
21174     simpleAccess: function(obj, subsc) {
21175         return obj[subsc];
21176     },
21177
21178         /**
21179          * @ignore
21180          */
21181     getJsonAccessor: function(){
21182         var re = /[\[\.]/;
21183         return function(expr) {
21184             try {
21185                 return(re.test(expr))
21186                     ? new Function("obj", "return obj." + expr)
21187                     : function(obj){
21188                         return obj[expr];
21189                     };
21190             } catch(e){}
21191             return Roo.emptyFn;
21192         };
21193     }(),
21194
21195     /**
21196      * Create a data block containing Roo.data.Records from an XML document.
21197      * @param {Object} o An object which contains an Array of row objects in the property specified
21198      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
21199      * which contains the total size of the dataset.
21200      * @return {Object} data A data block which is used by an Roo.data.Store object as
21201      * a cache of Roo.data.Records.
21202      */
21203     readRecords : function(o){
21204         /**
21205          * After any data loads, the raw JSON data is available for further custom processing.
21206          * @type Object
21207          */
21208         this.jsonData = o;
21209         var s = this.meta, Record = this.recordType,
21210             f = Record.prototype.fields, fi = f.items, fl = f.length;
21211
21212 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
21213         if (!this.ef) {
21214             if(s.totalProperty) {
21215                     this.getTotal = this.getJsonAccessor(s.totalProperty);
21216                 }
21217                 if(s.successProperty) {
21218                     this.getSuccess = this.getJsonAccessor(s.successProperty);
21219                 }
21220                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
21221                 if (s.id) {
21222                         var g = this.getJsonAccessor(s.id);
21223                         this.getId = function(rec) {
21224                                 var r = g(rec);
21225                                 return (r === undefined || r === "") ? null : r;
21226                         };
21227                 } else {
21228                         this.getId = function(){return null;};
21229                 }
21230             this.ef = [];
21231             for(var jj = 0; jj < fl; jj++){
21232                 f = fi[jj];
21233                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
21234                 this.ef[jj] = this.getJsonAccessor(map);
21235             }
21236         }
21237
21238         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
21239         if(s.totalProperty){
21240             var vt = parseInt(this.getTotal(o), 10);
21241             if(!isNaN(vt)){
21242                 totalRecords = vt;
21243             }
21244         }
21245         if(s.successProperty){
21246             var vs = this.getSuccess(o);
21247             if(vs === false || vs === 'false'){
21248                 success = false;
21249             }
21250         }
21251         var records = [];
21252             for(var i = 0; i < c; i++){
21253                     var n = root[i];
21254                 var values = {};
21255                 var id = this.getId(n);
21256                 for(var j = 0; j < fl; j++){
21257                     f = fi[j];
21258                 var v = this.ef[j](n);
21259                 if (!f.convert) {
21260                     Roo.log('missing convert for ' + f.name);
21261                     Roo.log(f);
21262                     continue;
21263                 }
21264                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
21265                 }
21266                 var record = new Record(values, id);
21267                 record.json = n;
21268                 records[i] = record;
21269             }
21270             return {
21271                 success : success,
21272                 records : records,
21273                 totalRecords : totalRecords
21274             };
21275     }
21276 });/*
21277  * Based on:
21278  * Ext JS Library 1.1.1
21279  * Copyright(c) 2006-2007, Ext JS, LLC.
21280  *
21281  * Originally Released Under LGPL - original licence link has changed is not relivant.
21282  *
21283  * Fork - LGPL
21284  * <script type="text/javascript">
21285  */
21286
21287 /**
21288  * @class Roo.data.XmlReader
21289  * @extends Roo.data.DataReader
21290  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
21291  * based on mappings in a provided Roo.data.Record constructor.<br><br>
21292  * <p>
21293  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
21294  * header in the HTTP response must be set to "text/xml".</em>
21295  * <p>
21296  * Example code:
21297  * <pre><code>
21298 var RecordDef = Roo.data.Record.create([
21299    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21300    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21301 ]);
21302 var myReader = new Roo.data.XmlReader({
21303    totalRecords: "results", // The element which contains the total dataset size (optional)
21304    record: "row",           // The repeated element which contains row information
21305    id: "id"                 // The element within the row that provides an ID for the record (optional)
21306 }, RecordDef);
21307 </code></pre>
21308  * <p>
21309  * This would consume an XML file like this:
21310  * <pre><code>
21311 &lt;?xml?>
21312 &lt;dataset>
21313  &lt;results>2&lt;/results>
21314  &lt;row>
21315    &lt;id>1&lt;/id>
21316    &lt;name>Bill&lt;/name>
21317    &lt;occupation>Gardener&lt;/occupation>
21318  &lt;/row>
21319  &lt;row>
21320    &lt;id>2&lt;/id>
21321    &lt;name>Ben&lt;/name>
21322    &lt;occupation>Horticulturalist&lt;/occupation>
21323  &lt;/row>
21324 &lt;/dataset>
21325 </code></pre>
21326  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
21327  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21328  * paged from the remote server.
21329  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
21330  * @cfg {String} success The DomQuery path to the success attribute used by forms.
21331  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
21332  * a record identifier value.
21333  * @constructor
21334  * Create a new XmlReader
21335  * @param {Object} meta Metadata configuration options
21336  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
21337  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
21338  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
21339  */
21340 Roo.data.XmlReader = function(meta, recordType){
21341     meta = meta || {};
21342     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21343 };
21344 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
21345     /**
21346      * This method is only used by a DataProxy which has retrieved data from a remote server.
21347          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
21348          * to contain a method called 'responseXML' that returns an XML document object.
21349      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21350      * a cache of Roo.data.Records.
21351      */
21352     read : function(response){
21353         var doc = response.responseXML;
21354         if(!doc) {
21355             throw {message: "XmlReader.read: XML Document not available"};
21356         }
21357         return this.readRecords(doc);
21358     },
21359
21360     /**
21361      * Create a data block containing Roo.data.Records from an XML document.
21362          * @param {Object} doc A parsed XML document.
21363      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21364      * a cache of Roo.data.Records.
21365      */
21366     readRecords : function(doc){
21367         /**
21368          * After any data loads/reads, the raw XML Document is available for further custom processing.
21369          * @type XMLDocument
21370          */
21371         this.xmlData = doc;
21372         var root = doc.documentElement || doc;
21373         var q = Roo.DomQuery;
21374         var recordType = this.recordType, fields = recordType.prototype.fields;
21375         var sid = this.meta.id;
21376         var totalRecords = 0, success = true;
21377         if(this.meta.totalRecords){
21378             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
21379         }
21380         
21381         if(this.meta.success){
21382             var sv = q.selectValue(this.meta.success, root, true);
21383             success = sv !== false && sv !== 'false';
21384         }
21385         var records = [];
21386         var ns = q.select(this.meta.record, root);
21387         for(var i = 0, len = ns.length; i < len; i++) {
21388                 var n = ns[i];
21389                 var values = {};
21390                 var id = sid ? q.selectValue(sid, n) : undefined;
21391                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21392                     var f = fields.items[j];
21393                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
21394                     v = f.convert(v);
21395                     values[f.name] = v;
21396                 }
21397                 var record = new recordType(values, id);
21398                 record.node = n;
21399                 records[records.length] = record;
21400             }
21401
21402             return {
21403                 success : success,
21404                 records : records,
21405                 totalRecords : totalRecords || records.length
21406             };
21407     }
21408 });/*
21409  * Based on:
21410  * Ext JS Library 1.1.1
21411  * Copyright(c) 2006-2007, Ext JS, LLC.
21412  *
21413  * Originally Released Under LGPL - original licence link has changed is not relivant.
21414  *
21415  * Fork - LGPL
21416  * <script type="text/javascript">
21417  */
21418
21419 /**
21420  * @class Roo.data.ArrayReader
21421  * @extends Roo.data.DataReader
21422  * Data reader class to create an Array of Roo.data.Record objects from an Array.
21423  * Each element of that Array represents a row of data fields. The
21424  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
21425  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
21426  * <p>
21427  * Example code:.
21428  * <pre><code>
21429 var RecordDef = Roo.data.Record.create([
21430     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
21431     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
21432 ]);
21433 var myReader = new Roo.data.ArrayReader({
21434     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
21435 }, RecordDef);
21436 </code></pre>
21437  * <p>
21438  * This would consume an Array like this:
21439  * <pre><code>
21440 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
21441   </code></pre>
21442  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
21443  * @constructor
21444  * Create a new JsonReader
21445  * @param {Object} meta Metadata configuration options.
21446  * @param {Object} recordType Either an Array of field definition objects
21447  * as specified to {@link Roo.data.Record#create},
21448  * or an {@link Roo.data.Record} object
21449  * created using {@link Roo.data.Record#create}.
21450  */
21451 Roo.data.ArrayReader = function(meta, recordType){
21452     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
21453 };
21454
21455 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
21456     /**
21457      * Create a data block containing Roo.data.Records from an XML document.
21458      * @param {Object} o An Array of row objects which represents the dataset.
21459      * @return {Object} data A data block which is used by an Roo.data.Store object as
21460      * a cache of Roo.data.Records.
21461      */
21462     readRecords : function(o){
21463         var sid = this.meta ? this.meta.id : null;
21464         var recordType = this.recordType, fields = recordType.prototype.fields;
21465         var records = [];
21466         var root = o;
21467             for(var i = 0; i < root.length; i++){
21468                     var n = root[i];
21469                 var values = {};
21470                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
21471                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21472                 var f = fields.items[j];
21473                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
21474                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
21475                 v = f.convert(v);
21476                 values[f.name] = v;
21477             }
21478                 var record = new recordType(values, id);
21479                 record.json = n;
21480                 records[records.length] = record;
21481             }
21482             return {
21483                 records : records,
21484                 totalRecords : records.length
21485             };
21486     }
21487 });/*
21488  * Based on:
21489  * Ext JS Library 1.1.1
21490  * Copyright(c) 2006-2007, Ext JS, LLC.
21491  *
21492  * Originally Released Under LGPL - original licence link has changed is not relivant.
21493  *
21494  * Fork - LGPL
21495  * <script type="text/javascript">
21496  */
21497
21498
21499 /**
21500  * @class Roo.data.Tree
21501  * @extends Roo.util.Observable
21502  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21503  * in the tree have most standard DOM functionality.
21504  * @constructor
21505  * @param {Node} root (optional) The root node
21506  */
21507 Roo.data.Tree = function(root){
21508    this.nodeHash = {};
21509    /**
21510     * The root node for this tree
21511     * @type Node
21512     */
21513    this.root = null;
21514    if(root){
21515        this.setRootNode(root);
21516    }
21517    this.addEvents({
21518        /**
21519         * @event append
21520         * Fires when a new child node is appended to a node in this tree.
21521         * @param {Tree} tree The owner tree
21522         * @param {Node} parent The parent node
21523         * @param {Node} node The newly appended node
21524         * @param {Number} index The index of the newly appended node
21525         */
21526        "append" : true,
21527        /**
21528         * @event remove
21529         * Fires when a child node is removed from a node in this tree.
21530         * @param {Tree} tree The owner tree
21531         * @param {Node} parent The parent node
21532         * @param {Node} node The child node removed
21533         */
21534        "remove" : true,
21535        /**
21536         * @event move
21537         * Fires when a node is moved to a new location in the tree
21538         * @param {Tree} tree The owner tree
21539         * @param {Node} node The node moved
21540         * @param {Node} oldParent The old parent of this node
21541         * @param {Node} newParent The new parent of this node
21542         * @param {Number} index The index it was moved to
21543         */
21544        "move" : true,
21545        /**
21546         * @event insert
21547         * Fires when a new child node is inserted in a node in this tree.
21548         * @param {Tree} tree The owner tree
21549         * @param {Node} parent The parent node
21550         * @param {Node} node The child node inserted
21551         * @param {Node} refNode The child node the node was inserted before
21552         */
21553        "insert" : true,
21554        /**
21555         * @event beforeappend
21556         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21557         * @param {Tree} tree The owner tree
21558         * @param {Node} parent The parent node
21559         * @param {Node} node The child node to be appended
21560         */
21561        "beforeappend" : true,
21562        /**
21563         * @event beforeremove
21564         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21565         * @param {Tree} tree The owner tree
21566         * @param {Node} parent The parent node
21567         * @param {Node} node The child node to be removed
21568         */
21569        "beforeremove" : true,
21570        /**
21571         * @event beforemove
21572         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21573         * @param {Tree} tree The owner tree
21574         * @param {Node} node The node being moved
21575         * @param {Node} oldParent The parent of the node
21576         * @param {Node} newParent The new parent the node is moving to
21577         * @param {Number} index The index it is being moved to
21578         */
21579        "beforemove" : true,
21580        /**
21581         * @event beforeinsert
21582         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21583         * @param {Tree} tree The owner tree
21584         * @param {Node} parent The parent node
21585         * @param {Node} node The child node to be inserted
21586         * @param {Node} refNode The child node the node is being inserted before
21587         */
21588        "beforeinsert" : true
21589    });
21590
21591     Roo.data.Tree.superclass.constructor.call(this);
21592 };
21593
21594 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21595     pathSeparator: "/",
21596
21597     proxyNodeEvent : function(){
21598         return this.fireEvent.apply(this, arguments);
21599     },
21600
21601     /**
21602      * Returns the root node for this tree.
21603      * @return {Node}
21604      */
21605     getRootNode : function(){
21606         return this.root;
21607     },
21608
21609     /**
21610      * Sets the root node for this tree.
21611      * @param {Node} node
21612      * @return {Node}
21613      */
21614     setRootNode : function(node){
21615         this.root = node;
21616         node.ownerTree = this;
21617         node.isRoot = true;
21618         this.registerNode(node);
21619         return node;
21620     },
21621
21622     /**
21623      * Gets a node in this tree by its id.
21624      * @param {String} id
21625      * @return {Node}
21626      */
21627     getNodeById : function(id){
21628         return this.nodeHash[id];
21629     },
21630
21631     registerNode : function(node){
21632         this.nodeHash[node.id] = node;
21633     },
21634
21635     unregisterNode : function(node){
21636         delete this.nodeHash[node.id];
21637     },
21638
21639     toString : function(){
21640         return "[Tree"+(this.id?" "+this.id:"")+"]";
21641     }
21642 });
21643
21644 /**
21645  * @class Roo.data.Node
21646  * @extends Roo.util.Observable
21647  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21648  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21649  * @constructor
21650  * @param {Object} attributes The attributes/config for the node
21651  */
21652 Roo.data.Node = function(attributes){
21653     /**
21654      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21655      * @type {Object}
21656      */
21657     this.attributes = attributes || {};
21658     this.leaf = this.attributes.leaf;
21659     /**
21660      * The node id. @type String
21661      */
21662     this.id = this.attributes.id;
21663     if(!this.id){
21664         this.id = Roo.id(null, "ynode-");
21665         this.attributes.id = this.id;
21666     }
21667      
21668     
21669     /**
21670      * All child nodes of this node. @type Array
21671      */
21672     this.childNodes = [];
21673     if(!this.childNodes.indexOf){ // indexOf is a must
21674         this.childNodes.indexOf = function(o){
21675             for(var i = 0, len = this.length; i < len; i++){
21676                 if(this[i] == o) {
21677                     return i;
21678                 }
21679             }
21680             return -1;
21681         };
21682     }
21683     /**
21684      * The parent node for this node. @type Node
21685      */
21686     this.parentNode = null;
21687     /**
21688      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21689      */
21690     this.firstChild = null;
21691     /**
21692      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21693      */
21694     this.lastChild = null;
21695     /**
21696      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21697      */
21698     this.previousSibling = null;
21699     /**
21700      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21701      */
21702     this.nextSibling = null;
21703
21704     this.addEvents({
21705        /**
21706         * @event append
21707         * Fires when a new child node is appended
21708         * @param {Tree} tree The owner tree
21709         * @param {Node} this This node
21710         * @param {Node} node The newly appended node
21711         * @param {Number} index The index of the newly appended node
21712         */
21713        "append" : true,
21714        /**
21715         * @event remove
21716         * Fires when a child node is removed
21717         * @param {Tree} tree The owner tree
21718         * @param {Node} this This node
21719         * @param {Node} node The removed node
21720         */
21721        "remove" : true,
21722        /**
21723         * @event move
21724         * Fires when this node is moved to a new location in the tree
21725         * @param {Tree} tree The owner tree
21726         * @param {Node} this This node
21727         * @param {Node} oldParent The old parent of this node
21728         * @param {Node} newParent The new parent of this node
21729         * @param {Number} index The index it was moved to
21730         */
21731        "move" : true,
21732        /**
21733         * @event insert
21734         * Fires when a new child node is inserted.
21735         * @param {Tree} tree The owner tree
21736         * @param {Node} this This node
21737         * @param {Node} node The child node inserted
21738         * @param {Node} refNode The child node the node was inserted before
21739         */
21740        "insert" : true,
21741        /**
21742         * @event beforeappend
21743         * Fires before a new child is appended, return false to cancel the append.
21744         * @param {Tree} tree The owner tree
21745         * @param {Node} this This node
21746         * @param {Node} node The child node to be appended
21747         */
21748        "beforeappend" : true,
21749        /**
21750         * @event beforeremove
21751         * Fires before a child is removed, return false to cancel the remove.
21752         * @param {Tree} tree The owner tree
21753         * @param {Node} this This node
21754         * @param {Node} node The child node to be removed
21755         */
21756        "beforeremove" : true,
21757        /**
21758         * @event beforemove
21759         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21760         * @param {Tree} tree The owner tree
21761         * @param {Node} this This node
21762         * @param {Node} oldParent The parent of this node
21763         * @param {Node} newParent The new parent this node is moving to
21764         * @param {Number} index The index it is being moved to
21765         */
21766        "beforemove" : true,
21767        /**
21768         * @event beforeinsert
21769         * Fires before a new child is inserted, return false to cancel the insert.
21770         * @param {Tree} tree The owner tree
21771         * @param {Node} this This node
21772         * @param {Node} node The child node to be inserted
21773         * @param {Node} refNode The child node the node is being inserted before
21774         */
21775        "beforeinsert" : true
21776    });
21777     this.listeners = this.attributes.listeners;
21778     Roo.data.Node.superclass.constructor.call(this);
21779 };
21780
21781 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21782     fireEvent : function(evtName){
21783         // first do standard event for this node
21784         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21785             return false;
21786         }
21787         // then bubble it up to the tree if the event wasn't cancelled
21788         var ot = this.getOwnerTree();
21789         if(ot){
21790             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21791                 return false;
21792             }
21793         }
21794         return true;
21795     },
21796
21797     /**
21798      * Returns true if this node is a leaf
21799      * @return {Boolean}
21800      */
21801     isLeaf : function(){
21802         return this.leaf === true;
21803     },
21804
21805     // private
21806     setFirstChild : function(node){
21807         this.firstChild = node;
21808     },
21809
21810     //private
21811     setLastChild : function(node){
21812         this.lastChild = node;
21813     },
21814
21815
21816     /**
21817      * Returns true if this node is the last child of its parent
21818      * @return {Boolean}
21819      */
21820     isLast : function(){
21821        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21822     },
21823
21824     /**
21825      * Returns true if this node is the first child of its parent
21826      * @return {Boolean}
21827      */
21828     isFirst : function(){
21829        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21830     },
21831
21832     hasChildNodes : function(){
21833         return !this.isLeaf() && this.childNodes.length > 0;
21834     },
21835
21836     /**
21837      * Insert node(s) as the last child node of this node.
21838      * @param {Node/Array} node The node or Array of nodes to append
21839      * @return {Node} The appended node if single append, or null if an array was passed
21840      */
21841     appendChild : function(node){
21842         var multi = false;
21843         if(node instanceof Array){
21844             multi = node;
21845         }else if(arguments.length > 1){
21846             multi = arguments;
21847         }
21848         // if passed an array or multiple args do them one by one
21849         if(multi){
21850             for(var i = 0, len = multi.length; i < len; i++) {
21851                 this.appendChild(multi[i]);
21852             }
21853         }else{
21854             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21855                 return false;
21856             }
21857             var index = this.childNodes.length;
21858             var oldParent = node.parentNode;
21859             // it's a move, make sure we move it cleanly
21860             if(oldParent){
21861                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21862                     return false;
21863                 }
21864                 oldParent.removeChild(node);
21865             }
21866             index = this.childNodes.length;
21867             if(index == 0){
21868                 this.setFirstChild(node);
21869             }
21870             this.childNodes.push(node);
21871             node.parentNode = this;
21872             var ps = this.childNodes[index-1];
21873             if(ps){
21874                 node.previousSibling = ps;
21875                 ps.nextSibling = node;
21876             }else{
21877                 node.previousSibling = null;
21878             }
21879             node.nextSibling = null;
21880             this.setLastChild(node);
21881             node.setOwnerTree(this.getOwnerTree());
21882             this.fireEvent("append", this.ownerTree, this, node, index);
21883             if(oldParent){
21884                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21885             }
21886             return node;
21887         }
21888     },
21889
21890     /**
21891      * Removes a child node from this node.
21892      * @param {Node} node The node to remove
21893      * @return {Node} The removed node
21894      */
21895     removeChild : function(node){
21896         var index = this.childNodes.indexOf(node);
21897         if(index == -1){
21898             return false;
21899         }
21900         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21901             return false;
21902         }
21903
21904         // remove it from childNodes collection
21905         this.childNodes.splice(index, 1);
21906
21907         // update siblings
21908         if(node.previousSibling){
21909             node.previousSibling.nextSibling = node.nextSibling;
21910         }
21911         if(node.nextSibling){
21912             node.nextSibling.previousSibling = node.previousSibling;
21913         }
21914
21915         // update child refs
21916         if(this.firstChild == node){
21917             this.setFirstChild(node.nextSibling);
21918         }
21919         if(this.lastChild == node){
21920             this.setLastChild(node.previousSibling);
21921         }
21922
21923         node.setOwnerTree(null);
21924         // clear any references from the node
21925         node.parentNode = null;
21926         node.previousSibling = null;
21927         node.nextSibling = null;
21928         this.fireEvent("remove", this.ownerTree, this, node);
21929         return node;
21930     },
21931
21932     /**
21933      * Inserts the first node before the second node in this nodes childNodes collection.
21934      * @param {Node} node The node to insert
21935      * @param {Node} refNode The node to insert before (if null the node is appended)
21936      * @return {Node} The inserted node
21937      */
21938     insertBefore : function(node, refNode){
21939         if(!refNode){ // like standard Dom, refNode can be null for append
21940             return this.appendChild(node);
21941         }
21942         // nothing to do
21943         if(node == refNode){
21944             return false;
21945         }
21946
21947         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21948             return false;
21949         }
21950         var index = this.childNodes.indexOf(refNode);
21951         var oldParent = node.parentNode;
21952         var refIndex = index;
21953
21954         // when moving internally, indexes will change after remove
21955         if(oldParent == this && this.childNodes.indexOf(node) < index){
21956             refIndex--;
21957         }
21958
21959         // it's a move, make sure we move it cleanly
21960         if(oldParent){
21961             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21962                 return false;
21963             }
21964             oldParent.removeChild(node);
21965         }
21966         if(refIndex == 0){
21967             this.setFirstChild(node);
21968         }
21969         this.childNodes.splice(refIndex, 0, node);
21970         node.parentNode = this;
21971         var ps = this.childNodes[refIndex-1];
21972         if(ps){
21973             node.previousSibling = ps;
21974             ps.nextSibling = node;
21975         }else{
21976             node.previousSibling = null;
21977         }
21978         node.nextSibling = refNode;
21979         refNode.previousSibling = node;
21980         node.setOwnerTree(this.getOwnerTree());
21981         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21982         if(oldParent){
21983             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21984         }
21985         return node;
21986     },
21987
21988     /**
21989      * Returns the child node at the specified index.
21990      * @param {Number} index
21991      * @return {Node}
21992      */
21993     item : function(index){
21994         return this.childNodes[index];
21995     },
21996
21997     /**
21998      * Replaces one child node in this node with another.
21999      * @param {Node} newChild The replacement node
22000      * @param {Node} oldChild The node to replace
22001      * @return {Node} The replaced node
22002      */
22003     replaceChild : function(newChild, oldChild){
22004         this.insertBefore(newChild, oldChild);
22005         this.removeChild(oldChild);
22006         return oldChild;
22007     },
22008
22009     /**
22010      * Returns the index of a child node
22011      * @param {Node} node
22012      * @return {Number} The index of the node or -1 if it was not found
22013      */
22014     indexOf : function(child){
22015         return this.childNodes.indexOf(child);
22016     },
22017
22018     /**
22019      * Returns the tree this node is in.
22020      * @return {Tree}
22021      */
22022     getOwnerTree : function(){
22023         // if it doesn't have one, look for one
22024         if(!this.ownerTree){
22025             var p = this;
22026             while(p){
22027                 if(p.ownerTree){
22028                     this.ownerTree = p.ownerTree;
22029                     break;
22030                 }
22031                 p = p.parentNode;
22032             }
22033         }
22034         return this.ownerTree;
22035     },
22036
22037     /**
22038      * Returns depth of this node (the root node has a depth of 0)
22039      * @return {Number}
22040      */
22041     getDepth : function(){
22042         var depth = 0;
22043         var p = this;
22044         while(p.parentNode){
22045             ++depth;
22046             p = p.parentNode;
22047         }
22048         return depth;
22049     },
22050
22051     // private
22052     setOwnerTree : function(tree){
22053         // if it's move, we need to update everyone
22054         if(tree != this.ownerTree){
22055             if(this.ownerTree){
22056                 this.ownerTree.unregisterNode(this);
22057             }
22058             this.ownerTree = tree;
22059             var cs = this.childNodes;
22060             for(var i = 0, len = cs.length; i < len; i++) {
22061                 cs[i].setOwnerTree(tree);
22062             }
22063             if(tree){
22064                 tree.registerNode(this);
22065             }
22066         }
22067     },
22068
22069     /**
22070      * Returns the path for this node. The path can be used to expand or select this node programmatically.
22071      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
22072      * @return {String} The path
22073      */
22074     getPath : function(attr){
22075         attr = attr || "id";
22076         var p = this.parentNode;
22077         var b = [this.attributes[attr]];
22078         while(p){
22079             b.unshift(p.attributes[attr]);
22080             p = p.parentNode;
22081         }
22082         var sep = this.getOwnerTree().pathSeparator;
22083         return sep + b.join(sep);
22084     },
22085
22086     /**
22087      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22088      * function call will be the scope provided or the current node. The arguments to the function
22089      * will be the args provided or the current node. If the function returns false at any point,
22090      * the bubble is stopped.
22091      * @param {Function} fn The function to call
22092      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22093      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22094      */
22095     bubble : function(fn, scope, args){
22096         var p = this;
22097         while(p){
22098             if(fn.call(scope || p, args || p) === false){
22099                 break;
22100             }
22101             p = p.parentNode;
22102         }
22103     },
22104
22105     /**
22106      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22107      * function call will be the scope provided or the current node. The arguments to the function
22108      * will be the args provided or the current node. If the function returns false at any point,
22109      * the cascade is stopped on that branch.
22110      * @param {Function} fn The function to call
22111      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22112      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22113      */
22114     cascade : function(fn, scope, args){
22115         if(fn.call(scope || this, args || this) !== false){
22116             var cs = this.childNodes;
22117             for(var i = 0, len = cs.length; i < len; i++) {
22118                 cs[i].cascade(fn, scope, args);
22119             }
22120         }
22121     },
22122
22123     /**
22124      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
22125      * function call will be the scope provided or the current node. The arguments to the function
22126      * will be the args provided or the current node. If the function returns false at any point,
22127      * the iteration stops.
22128      * @param {Function} fn The function to call
22129      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22130      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22131      */
22132     eachChild : function(fn, scope, args){
22133         var cs = this.childNodes;
22134         for(var i = 0, len = cs.length; i < len; i++) {
22135                 if(fn.call(scope || this, args || cs[i]) === false){
22136                     break;
22137                 }
22138         }
22139     },
22140
22141     /**
22142      * Finds the first child that has the attribute with the specified value.
22143      * @param {String} attribute The attribute name
22144      * @param {Mixed} value The value to search for
22145      * @return {Node} The found child or null if none was found
22146      */
22147     findChild : function(attribute, value){
22148         var cs = this.childNodes;
22149         for(var i = 0, len = cs.length; i < len; i++) {
22150                 if(cs[i].attributes[attribute] == value){
22151                     return cs[i];
22152                 }
22153         }
22154         return null;
22155     },
22156
22157     /**
22158      * Finds the first child by a custom function. The child matches if the function passed
22159      * returns true.
22160      * @param {Function} fn
22161      * @param {Object} scope (optional)
22162      * @return {Node} The found child or null if none was found
22163      */
22164     findChildBy : function(fn, scope){
22165         var cs = this.childNodes;
22166         for(var i = 0, len = cs.length; i < len; i++) {
22167                 if(fn.call(scope||cs[i], cs[i]) === true){
22168                     return cs[i];
22169                 }
22170         }
22171         return null;
22172     },
22173
22174     /**
22175      * Sorts this nodes children using the supplied sort function
22176      * @param {Function} fn
22177      * @param {Object} scope (optional)
22178      */
22179     sort : function(fn, scope){
22180         var cs = this.childNodes;
22181         var len = cs.length;
22182         if(len > 0){
22183             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
22184             cs.sort(sortFn);
22185             for(var i = 0; i < len; i++){
22186                 var n = cs[i];
22187                 n.previousSibling = cs[i-1];
22188                 n.nextSibling = cs[i+1];
22189                 if(i == 0){
22190                     this.setFirstChild(n);
22191                 }
22192                 if(i == len-1){
22193                     this.setLastChild(n);
22194                 }
22195             }
22196         }
22197     },
22198
22199     /**
22200      * Returns true if this node is an ancestor (at any point) of the passed node.
22201      * @param {Node} node
22202      * @return {Boolean}
22203      */
22204     contains : function(node){
22205         return node.isAncestor(this);
22206     },
22207
22208     /**
22209      * Returns true if the passed node is an ancestor (at any point) of this node.
22210      * @param {Node} node
22211      * @return {Boolean}
22212      */
22213     isAncestor : function(node){
22214         var p = this.parentNode;
22215         while(p){
22216             if(p == node){
22217                 return true;
22218             }
22219             p = p.parentNode;
22220         }
22221         return false;
22222     },
22223
22224     toString : function(){
22225         return "[Node"+(this.id?" "+this.id:"")+"]";
22226     }
22227 });/*
22228  * Based on:
22229  * Ext JS Library 1.1.1
22230  * Copyright(c) 2006-2007, Ext JS, LLC.
22231  *
22232  * Originally Released Under LGPL - original licence link has changed is not relivant.
22233  *
22234  * Fork - LGPL
22235  * <script type="text/javascript">
22236  */
22237  
22238
22239 /**
22240  * @class Roo.ComponentMgr
22241  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
22242  * @singleton
22243  */
22244 Roo.ComponentMgr = function(){
22245     var all = new Roo.util.MixedCollection();
22246
22247     return {
22248         /**
22249          * Registers a component.
22250          * @param {Roo.Component} c The component
22251          */
22252         register : function(c){
22253             all.add(c);
22254         },
22255
22256         /**
22257          * Unregisters a component.
22258          * @param {Roo.Component} c The component
22259          */
22260         unregister : function(c){
22261             all.remove(c);
22262         },
22263
22264         /**
22265          * Returns a component by id
22266          * @param {String} id The component id
22267          */
22268         get : function(id){
22269             return all.get(id);
22270         },
22271
22272         /**
22273          * Registers a function that will be called when a specified component is added to ComponentMgr
22274          * @param {String} id The component id
22275          * @param {Funtction} fn The callback function
22276          * @param {Object} scope The scope of the callback
22277          */
22278         onAvailable : function(id, fn, scope){
22279             all.on("add", function(index, o){
22280                 if(o.id == id){
22281                     fn.call(scope || o, o);
22282                     all.un("add", fn, scope);
22283                 }
22284             });
22285         }
22286     };
22287 }();/*
22288  * Based on:
22289  * Ext JS Library 1.1.1
22290  * Copyright(c) 2006-2007, Ext JS, LLC.
22291  *
22292  * Originally Released Under LGPL - original licence link has changed is not relivant.
22293  *
22294  * Fork - LGPL
22295  * <script type="text/javascript">
22296  */
22297  
22298 /**
22299  * @class Roo.Component
22300  * @extends Roo.util.Observable
22301  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
22302  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
22303  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
22304  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
22305  * All visual components (widgets) that require rendering into a layout should subclass Component.
22306  * @constructor
22307  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
22308  * 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
22309  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
22310  */
22311 Roo.Component = function(config){
22312     config = config || {};
22313     if(config.tagName || config.dom || typeof config == "string"){ // element object
22314         config = {el: config, id: config.id || config};
22315     }
22316     this.initialConfig = config;
22317
22318     Roo.apply(this, config);
22319     this.addEvents({
22320         /**
22321          * @event disable
22322          * Fires after the component is disabled.
22323              * @param {Roo.Component} this
22324              */
22325         disable : true,
22326         /**
22327          * @event enable
22328          * Fires after the component is enabled.
22329              * @param {Roo.Component} this
22330              */
22331         enable : true,
22332         /**
22333          * @event beforeshow
22334          * Fires before the component is shown.  Return false to stop the show.
22335              * @param {Roo.Component} this
22336              */
22337         beforeshow : true,
22338         /**
22339          * @event show
22340          * Fires after the component is shown.
22341              * @param {Roo.Component} this
22342              */
22343         show : true,
22344         /**
22345          * @event beforehide
22346          * Fires before the component is hidden. Return false to stop the hide.
22347              * @param {Roo.Component} this
22348              */
22349         beforehide : true,
22350         /**
22351          * @event hide
22352          * Fires after the component is hidden.
22353              * @param {Roo.Component} this
22354              */
22355         hide : true,
22356         /**
22357          * @event beforerender
22358          * Fires before the component is rendered. Return false to stop the render.
22359              * @param {Roo.Component} this
22360              */
22361         beforerender : true,
22362         /**
22363          * @event render
22364          * Fires after the component is rendered.
22365              * @param {Roo.Component} this
22366              */
22367         render : true,
22368         /**
22369          * @event beforedestroy
22370          * Fires before the component is destroyed. Return false to stop the destroy.
22371              * @param {Roo.Component} this
22372              */
22373         beforedestroy : true,
22374         /**
22375          * @event destroy
22376          * Fires after the component is destroyed.
22377              * @param {Roo.Component} this
22378              */
22379         destroy : true
22380     });
22381     if(!this.id){
22382         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
22383     }
22384     Roo.ComponentMgr.register(this);
22385     Roo.Component.superclass.constructor.call(this);
22386     this.initComponent();
22387     if(this.renderTo){ // not supported by all components yet. use at your own risk!
22388         this.render(this.renderTo);
22389         delete this.renderTo;
22390     }
22391 };
22392
22393 /** @private */
22394 Roo.Component.AUTO_ID = 1000;
22395
22396 Roo.extend(Roo.Component, Roo.util.Observable, {
22397     /**
22398      * @scope Roo.Component.prototype
22399      * @type {Boolean}
22400      * true if this component is hidden. Read-only.
22401      */
22402     hidden : false,
22403     /**
22404      * @type {Boolean}
22405      * true if this component is disabled. Read-only.
22406      */
22407     disabled : false,
22408     /**
22409      * @type {Boolean}
22410      * true if this component has been rendered. Read-only.
22411      */
22412     rendered : false,
22413     
22414     /** @cfg {String} disableClass
22415      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
22416      */
22417     disabledClass : "x-item-disabled",
22418         /** @cfg {Boolean} allowDomMove
22419          * Whether the component can move the Dom node when rendering (defaults to true).
22420          */
22421     allowDomMove : true,
22422     /** @cfg {String} hideMode
22423      * How this component should hidden. Supported values are
22424      * "visibility" (css visibility), "offsets" (negative offset position) and
22425      * "display" (css display) - defaults to "display".
22426      */
22427     hideMode: 'display',
22428
22429     /** @private */
22430     ctype : "Roo.Component",
22431
22432     /**
22433      * @cfg {String} actionMode 
22434      * which property holds the element that used for  hide() / show() / disable() / enable()
22435      * default is 'el' 
22436      */
22437     actionMode : "el",
22438
22439     /** @private */
22440     getActionEl : function(){
22441         return this[this.actionMode];
22442     },
22443
22444     initComponent : Roo.emptyFn,
22445     /**
22446      * If this is a lazy rendering component, render it to its container element.
22447      * @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.
22448      */
22449     render : function(container, position){
22450         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
22451             if(!container && this.el){
22452                 this.el = Roo.get(this.el);
22453                 container = this.el.dom.parentNode;
22454                 this.allowDomMove = false;
22455             }
22456             this.container = Roo.get(container);
22457             this.rendered = true;
22458             if(position !== undefined){
22459                 if(typeof position == 'number'){
22460                     position = this.container.dom.childNodes[position];
22461                 }else{
22462                     position = Roo.getDom(position);
22463                 }
22464             }
22465             this.onRender(this.container, position || null);
22466             if(this.cls){
22467                 this.el.addClass(this.cls);
22468                 delete this.cls;
22469             }
22470             if(this.style){
22471                 this.el.applyStyles(this.style);
22472                 delete this.style;
22473             }
22474             this.fireEvent("render", this);
22475             this.afterRender(this.container);
22476             if(this.hidden){
22477                 this.hide();
22478             }
22479             if(this.disabled){
22480                 this.disable();
22481             }
22482         }
22483         return this;
22484     },
22485
22486     /** @private */
22487     // default function is not really useful
22488     onRender : function(ct, position){
22489         if(this.el){
22490             this.el = Roo.get(this.el);
22491             if(this.allowDomMove !== false){
22492                 ct.dom.insertBefore(this.el.dom, position);
22493             }
22494         }
22495     },
22496
22497     /** @private */
22498     getAutoCreate : function(){
22499         var cfg = typeof this.autoCreate == "object" ?
22500                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
22501         if(this.id && !cfg.id){
22502             cfg.id = this.id;
22503         }
22504         return cfg;
22505     },
22506
22507     /** @private */
22508     afterRender : Roo.emptyFn,
22509
22510     /**
22511      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22512      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22513      */
22514     destroy : function(){
22515         if(this.fireEvent("beforedestroy", this) !== false){
22516             this.purgeListeners();
22517             this.beforeDestroy();
22518             if(this.rendered){
22519                 this.el.removeAllListeners();
22520                 this.el.remove();
22521                 if(this.actionMode == "container"){
22522                     this.container.remove();
22523                 }
22524             }
22525             this.onDestroy();
22526             Roo.ComponentMgr.unregister(this);
22527             this.fireEvent("destroy", this);
22528         }
22529     },
22530
22531         /** @private */
22532     beforeDestroy : function(){
22533
22534     },
22535
22536         /** @private */
22537         onDestroy : function(){
22538
22539     },
22540
22541     /**
22542      * Returns the underlying {@link Roo.Element}.
22543      * @return {Roo.Element} The element
22544      */
22545     getEl : function(){
22546         return this.el;
22547     },
22548
22549     /**
22550      * Returns the id of this component.
22551      * @return {String}
22552      */
22553     getId : function(){
22554         return this.id;
22555     },
22556
22557     /**
22558      * Try to focus this component.
22559      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22560      * @return {Roo.Component} this
22561      */
22562     focus : function(selectText){
22563         if(this.rendered){
22564             this.el.focus();
22565             if(selectText === true){
22566                 this.el.dom.select();
22567             }
22568         }
22569         return this;
22570     },
22571
22572     /** @private */
22573     blur : function(){
22574         if(this.rendered){
22575             this.el.blur();
22576         }
22577         return this;
22578     },
22579
22580     /**
22581      * Disable this component.
22582      * @return {Roo.Component} this
22583      */
22584     disable : function(){
22585         if(this.rendered){
22586             this.onDisable();
22587         }
22588         this.disabled = true;
22589         this.fireEvent("disable", this);
22590         return this;
22591     },
22592
22593         // private
22594     onDisable : function(){
22595         this.getActionEl().addClass(this.disabledClass);
22596         this.el.dom.disabled = true;
22597     },
22598
22599     /**
22600      * Enable this component.
22601      * @return {Roo.Component} this
22602      */
22603     enable : function(){
22604         if(this.rendered){
22605             this.onEnable();
22606         }
22607         this.disabled = false;
22608         this.fireEvent("enable", this);
22609         return this;
22610     },
22611
22612         // private
22613     onEnable : function(){
22614         this.getActionEl().removeClass(this.disabledClass);
22615         this.el.dom.disabled = false;
22616     },
22617
22618     /**
22619      * Convenience function for setting disabled/enabled by boolean.
22620      * @param {Boolean} disabled
22621      */
22622     setDisabled : function(disabled){
22623         this[disabled ? "disable" : "enable"]();
22624     },
22625
22626     /**
22627      * Show this component.
22628      * @return {Roo.Component} this
22629      */
22630     show: function(){
22631         if(this.fireEvent("beforeshow", this) !== false){
22632             this.hidden = false;
22633             if(this.rendered){
22634                 this.onShow();
22635             }
22636             this.fireEvent("show", this);
22637         }
22638         return this;
22639     },
22640
22641     // private
22642     onShow : function(){
22643         var ae = this.getActionEl();
22644         if(this.hideMode == 'visibility'){
22645             ae.dom.style.visibility = "visible";
22646         }else if(this.hideMode == 'offsets'){
22647             ae.removeClass('x-hidden');
22648         }else{
22649             ae.dom.style.display = "";
22650         }
22651     },
22652
22653     /**
22654      * Hide this component.
22655      * @return {Roo.Component} this
22656      */
22657     hide: function(){
22658         if(this.fireEvent("beforehide", this) !== false){
22659             this.hidden = true;
22660             if(this.rendered){
22661                 this.onHide();
22662             }
22663             this.fireEvent("hide", this);
22664         }
22665         return this;
22666     },
22667
22668     // private
22669     onHide : function(){
22670         var ae = this.getActionEl();
22671         if(this.hideMode == 'visibility'){
22672             ae.dom.style.visibility = "hidden";
22673         }else if(this.hideMode == 'offsets'){
22674             ae.addClass('x-hidden');
22675         }else{
22676             ae.dom.style.display = "none";
22677         }
22678     },
22679
22680     /**
22681      * Convenience function to hide or show this component by boolean.
22682      * @param {Boolean} visible True to show, false to hide
22683      * @return {Roo.Component} this
22684      */
22685     setVisible: function(visible){
22686         if(visible) {
22687             this.show();
22688         }else{
22689             this.hide();
22690         }
22691         return this;
22692     },
22693
22694     /**
22695      * Returns true if this component is visible.
22696      */
22697     isVisible : function(){
22698         return this.getActionEl().isVisible();
22699     },
22700
22701     cloneConfig : function(overrides){
22702         overrides = overrides || {};
22703         var id = overrides.id || Roo.id();
22704         var cfg = Roo.applyIf(overrides, this.initialConfig);
22705         cfg.id = id; // prevent dup id
22706         return new this.constructor(cfg);
22707     }
22708 });/*
22709  * Based on:
22710  * Ext JS Library 1.1.1
22711  * Copyright(c) 2006-2007, Ext JS, LLC.
22712  *
22713  * Originally Released Under LGPL - original licence link has changed is not relivant.
22714  *
22715  * Fork - LGPL
22716  * <script type="text/javascript">
22717  */
22718  (function(){ 
22719 /**
22720  * @class Roo.Layer
22721  * @extends Roo.Element
22722  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22723  * automatic maintaining of shadow/shim positions.
22724  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22725  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22726  * you can pass a string with a CSS class name. False turns off the shadow.
22727  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22728  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22729  * @cfg {String} cls CSS class to add to the element
22730  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22731  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22732  * @constructor
22733  * @param {Object} config An object with config options.
22734  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22735  */
22736
22737 Roo.Layer = function(config, existingEl){
22738     config = config || {};
22739     var dh = Roo.DomHelper;
22740     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22741     if(existingEl){
22742         this.dom = Roo.getDom(existingEl);
22743     }
22744     if(!this.dom){
22745         var o = config.dh || {tag: "div", cls: "x-layer"};
22746         this.dom = dh.append(pel, o);
22747     }
22748     if(config.cls){
22749         this.addClass(config.cls);
22750     }
22751     this.constrain = config.constrain !== false;
22752     this.visibilityMode = Roo.Element.VISIBILITY;
22753     if(config.id){
22754         this.id = this.dom.id = config.id;
22755     }else{
22756         this.id = Roo.id(this.dom);
22757     }
22758     this.zindex = config.zindex || this.getZIndex();
22759     this.position("absolute", this.zindex);
22760     if(config.shadow){
22761         this.shadowOffset = config.shadowOffset || 4;
22762         this.shadow = new Roo.Shadow({
22763             offset : this.shadowOffset,
22764             mode : config.shadow
22765         });
22766     }else{
22767         this.shadowOffset = 0;
22768     }
22769     this.useShim = config.shim !== false && Roo.useShims;
22770     this.useDisplay = config.useDisplay;
22771     this.hide();
22772 };
22773
22774 var supr = Roo.Element.prototype;
22775
22776 // shims are shared among layer to keep from having 100 iframes
22777 var shims = [];
22778
22779 Roo.extend(Roo.Layer, Roo.Element, {
22780
22781     getZIndex : function(){
22782         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22783     },
22784
22785     getShim : function(){
22786         if(!this.useShim){
22787             return null;
22788         }
22789         if(this.shim){
22790             return this.shim;
22791         }
22792         var shim = shims.shift();
22793         if(!shim){
22794             shim = this.createShim();
22795             shim.enableDisplayMode('block');
22796             shim.dom.style.display = 'none';
22797             shim.dom.style.visibility = 'visible';
22798         }
22799         var pn = this.dom.parentNode;
22800         if(shim.dom.parentNode != pn){
22801             pn.insertBefore(shim.dom, this.dom);
22802         }
22803         shim.setStyle('z-index', this.getZIndex()-2);
22804         this.shim = shim;
22805         return shim;
22806     },
22807
22808     hideShim : function(){
22809         if(this.shim){
22810             this.shim.setDisplayed(false);
22811             shims.push(this.shim);
22812             delete this.shim;
22813         }
22814     },
22815
22816     disableShadow : function(){
22817         if(this.shadow){
22818             this.shadowDisabled = true;
22819             this.shadow.hide();
22820             this.lastShadowOffset = this.shadowOffset;
22821             this.shadowOffset = 0;
22822         }
22823     },
22824
22825     enableShadow : function(show){
22826         if(this.shadow){
22827             this.shadowDisabled = false;
22828             this.shadowOffset = this.lastShadowOffset;
22829             delete this.lastShadowOffset;
22830             if(show){
22831                 this.sync(true);
22832             }
22833         }
22834     },
22835
22836     // private
22837     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22838     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22839     sync : function(doShow){
22840         var sw = this.shadow;
22841         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22842             var sh = this.getShim();
22843
22844             var w = this.getWidth(),
22845                 h = this.getHeight();
22846
22847             var l = this.getLeft(true),
22848                 t = this.getTop(true);
22849
22850             if(sw && !this.shadowDisabled){
22851                 if(doShow && !sw.isVisible()){
22852                     sw.show(this);
22853                 }else{
22854                     sw.realign(l, t, w, h);
22855                 }
22856                 if(sh){
22857                     if(doShow){
22858                        sh.show();
22859                     }
22860                     // fit the shim behind the shadow, so it is shimmed too
22861                     var a = sw.adjusts, s = sh.dom.style;
22862                     s.left = (Math.min(l, l+a.l))+"px";
22863                     s.top = (Math.min(t, t+a.t))+"px";
22864                     s.width = (w+a.w)+"px";
22865                     s.height = (h+a.h)+"px";
22866                 }
22867             }else if(sh){
22868                 if(doShow){
22869                    sh.show();
22870                 }
22871                 sh.setSize(w, h);
22872                 sh.setLeftTop(l, t);
22873             }
22874             
22875         }
22876     },
22877
22878     // private
22879     destroy : function(){
22880         this.hideShim();
22881         if(this.shadow){
22882             this.shadow.hide();
22883         }
22884         this.removeAllListeners();
22885         var pn = this.dom.parentNode;
22886         if(pn){
22887             pn.removeChild(this.dom);
22888         }
22889         Roo.Element.uncache(this.id);
22890     },
22891
22892     remove : function(){
22893         this.destroy();
22894     },
22895
22896     // private
22897     beginUpdate : function(){
22898         this.updating = true;
22899     },
22900
22901     // private
22902     endUpdate : function(){
22903         this.updating = false;
22904         this.sync(true);
22905     },
22906
22907     // private
22908     hideUnders : function(negOffset){
22909         if(this.shadow){
22910             this.shadow.hide();
22911         }
22912         this.hideShim();
22913     },
22914
22915     // private
22916     constrainXY : function(){
22917         if(this.constrain){
22918             var vw = Roo.lib.Dom.getViewWidth(),
22919                 vh = Roo.lib.Dom.getViewHeight();
22920             var s = Roo.get(document).getScroll();
22921
22922             var xy = this.getXY();
22923             var x = xy[0], y = xy[1];   
22924             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22925             // only move it if it needs it
22926             var moved = false;
22927             // first validate right/bottom
22928             if((x + w) > vw+s.left){
22929                 x = vw - w - this.shadowOffset;
22930                 moved = true;
22931             }
22932             if((y + h) > vh+s.top){
22933                 y = vh - h - this.shadowOffset;
22934                 moved = true;
22935             }
22936             // then make sure top/left isn't negative
22937             if(x < s.left){
22938                 x = s.left;
22939                 moved = true;
22940             }
22941             if(y < s.top){
22942                 y = s.top;
22943                 moved = true;
22944             }
22945             if(moved){
22946                 if(this.avoidY){
22947                     var ay = this.avoidY;
22948                     if(y <= ay && (y+h) >= ay){
22949                         y = ay-h-5;   
22950                     }
22951                 }
22952                 xy = [x, y];
22953                 this.storeXY(xy);
22954                 supr.setXY.call(this, xy);
22955                 this.sync();
22956             }
22957         }
22958     },
22959
22960     isVisible : function(){
22961         return this.visible;    
22962     },
22963
22964     // private
22965     showAction : function(){
22966         this.visible = true; // track visibility to prevent getStyle calls
22967         if(this.useDisplay === true){
22968             this.setDisplayed("");
22969         }else if(this.lastXY){
22970             supr.setXY.call(this, this.lastXY);
22971         }else if(this.lastLT){
22972             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22973         }
22974     },
22975
22976     // private
22977     hideAction : function(){
22978         this.visible = false;
22979         if(this.useDisplay === true){
22980             this.setDisplayed(false);
22981         }else{
22982             this.setLeftTop(-10000,-10000);
22983         }
22984     },
22985
22986     // overridden Element method
22987     setVisible : function(v, a, d, c, e){
22988         if(v){
22989             this.showAction();
22990         }
22991         if(a && v){
22992             var cb = function(){
22993                 this.sync(true);
22994                 if(c){
22995                     c();
22996                 }
22997             }.createDelegate(this);
22998             supr.setVisible.call(this, true, true, d, cb, e);
22999         }else{
23000             if(!v){
23001                 this.hideUnders(true);
23002             }
23003             var cb = c;
23004             if(a){
23005                 cb = function(){
23006                     this.hideAction();
23007                     if(c){
23008                         c();
23009                     }
23010                 }.createDelegate(this);
23011             }
23012             supr.setVisible.call(this, v, a, d, cb, e);
23013             if(v){
23014                 this.sync(true);
23015             }else if(!a){
23016                 this.hideAction();
23017             }
23018         }
23019     },
23020
23021     storeXY : function(xy){
23022         delete this.lastLT;
23023         this.lastXY = xy;
23024     },
23025
23026     storeLeftTop : function(left, top){
23027         delete this.lastXY;
23028         this.lastLT = [left, top];
23029     },
23030
23031     // private
23032     beforeFx : function(){
23033         this.beforeAction();
23034         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23035     },
23036
23037     // private
23038     afterFx : function(){
23039         Roo.Layer.superclass.afterFx.apply(this, arguments);
23040         this.sync(this.isVisible());
23041     },
23042
23043     // private
23044     beforeAction : function(){
23045         if(!this.updating && this.shadow){
23046             this.shadow.hide();
23047         }
23048     },
23049
23050     // overridden Element method
23051     setLeft : function(left){
23052         this.storeLeftTop(left, this.getTop(true));
23053         supr.setLeft.apply(this, arguments);
23054         this.sync();
23055     },
23056
23057     setTop : function(top){
23058         this.storeLeftTop(this.getLeft(true), top);
23059         supr.setTop.apply(this, arguments);
23060         this.sync();
23061     },
23062
23063     setLeftTop : function(left, top){
23064         this.storeLeftTop(left, top);
23065         supr.setLeftTop.apply(this, arguments);
23066         this.sync();
23067     },
23068
23069     setXY : function(xy, a, d, c, e){
23070         this.fixDisplay();
23071         this.beforeAction();
23072         this.storeXY(xy);
23073         var cb = this.createCB(c);
23074         supr.setXY.call(this, xy, a, d, cb, e);
23075         if(!a){
23076             cb();
23077         }
23078     },
23079
23080     // private
23081     createCB : function(c){
23082         var el = this;
23083         return function(){
23084             el.constrainXY();
23085             el.sync(true);
23086             if(c){
23087                 c();
23088             }
23089         };
23090     },
23091
23092     // overridden Element method
23093     setX : function(x, a, d, c, e){
23094         this.setXY([x, this.getY()], a, d, c, e);
23095     },
23096
23097     // overridden Element method
23098     setY : function(y, a, d, c, e){
23099         this.setXY([this.getX(), y], a, d, c, e);
23100     },
23101
23102     // overridden Element method
23103     setSize : function(w, h, a, d, c, e){
23104         this.beforeAction();
23105         var cb = this.createCB(c);
23106         supr.setSize.call(this, w, h, a, d, cb, e);
23107         if(!a){
23108             cb();
23109         }
23110     },
23111
23112     // overridden Element method
23113     setWidth : function(w, a, d, c, e){
23114         this.beforeAction();
23115         var cb = this.createCB(c);
23116         supr.setWidth.call(this, w, a, d, cb, e);
23117         if(!a){
23118             cb();
23119         }
23120     },
23121
23122     // overridden Element method
23123     setHeight : function(h, a, d, c, e){
23124         this.beforeAction();
23125         var cb = this.createCB(c);
23126         supr.setHeight.call(this, h, a, d, cb, e);
23127         if(!a){
23128             cb();
23129         }
23130     },
23131
23132     // overridden Element method
23133     setBounds : function(x, y, w, h, a, d, c, e){
23134         this.beforeAction();
23135         var cb = this.createCB(c);
23136         if(!a){
23137             this.storeXY([x, y]);
23138             supr.setXY.call(this, [x, y]);
23139             supr.setSize.call(this, w, h, a, d, cb, e);
23140             cb();
23141         }else{
23142             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
23143         }
23144         return this;
23145     },
23146     
23147     /**
23148      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
23149      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
23150      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
23151      * @param {Number} zindex The new z-index to set
23152      * @return {this} The Layer
23153      */
23154     setZIndex : function(zindex){
23155         this.zindex = zindex;
23156         this.setStyle("z-index", zindex + 2);
23157         if(this.shadow){
23158             this.shadow.setZIndex(zindex + 1);
23159         }
23160         if(this.shim){
23161             this.shim.setStyle("z-index", zindex);
23162         }
23163     }
23164 });
23165 })();/*
23166  * Based on:
23167  * Ext JS Library 1.1.1
23168  * Copyright(c) 2006-2007, Ext JS, LLC.
23169  *
23170  * Originally Released Under LGPL - original licence link has changed is not relivant.
23171  *
23172  * Fork - LGPL
23173  * <script type="text/javascript">
23174  */
23175
23176
23177 /**
23178  * @class Roo.Shadow
23179  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
23180  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
23181  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
23182  * @constructor
23183  * Create a new Shadow
23184  * @param {Object} config The config object
23185  */
23186 Roo.Shadow = function(config){
23187     Roo.apply(this, config);
23188     if(typeof this.mode != "string"){
23189         this.mode = this.defaultMode;
23190     }
23191     var o = this.offset, a = {h: 0};
23192     var rad = Math.floor(this.offset/2);
23193     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
23194         case "drop":
23195             a.w = 0;
23196             a.l = a.t = o;
23197             a.t -= 1;
23198             if(Roo.isIE){
23199                 a.l -= this.offset + rad;
23200                 a.t -= this.offset + rad;
23201                 a.w -= rad;
23202                 a.h -= rad;
23203                 a.t += 1;
23204             }
23205         break;
23206         case "sides":
23207             a.w = (o*2);
23208             a.l = -o;
23209             a.t = o-1;
23210             if(Roo.isIE){
23211                 a.l -= (this.offset - rad);
23212                 a.t -= this.offset + rad;
23213                 a.l += 1;
23214                 a.w -= (this.offset - rad)*2;
23215                 a.w -= rad + 1;
23216                 a.h -= 1;
23217             }
23218         break;
23219         case "frame":
23220             a.w = a.h = (o*2);
23221             a.l = a.t = -o;
23222             a.t += 1;
23223             a.h -= 2;
23224             if(Roo.isIE){
23225                 a.l -= (this.offset - rad);
23226                 a.t -= (this.offset - rad);
23227                 a.l += 1;
23228                 a.w -= (this.offset + rad + 1);
23229                 a.h -= (this.offset + rad);
23230                 a.h += 1;
23231             }
23232         break;
23233     };
23234
23235     this.adjusts = a;
23236 };
23237
23238 Roo.Shadow.prototype = {
23239     /**
23240      * @cfg {String} mode
23241      * The shadow display mode.  Supports the following options:<br />
23242      * sides: Shadow displays on both sides and bottom only<br />
23243      * frame: Shadow displays equally on all four sides<br />
23244      * drop: Traditional bottom-right drop shadow (default)
23245      */
23246     /**
23247      * @cfg {String} offset
23248      * The number of pixels to offset the shadow from the element (defaults to 4)
23249      */
23250     offset: 4,
23251
23252     // private
23253     defaultMode: "drop",
23254
23255     /**
23256      * Displays the shadow under the target element
23257      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
23258      */
23259     show : function(target){
23260         target = Roo.get(target);
23261         if(!this.el){
23262             this.el = Roo.Shadow.Pool.pull();
23263             if(this.el.dom.nextSibling != target.dom){
23264                 this.el.insertBefore(target);
23265             }
23266         }
23267         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
23268         if(Roo.isIE){
23269             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
23270         }
23271         this.realign(
23272             target.getLeft(true),
23273             target.getTop(true),
23274             target.getWidth(),
23275             target.getHeight()
23276         );
23277         this.el.dom.style.display = "block";
23278     },
23279
23280     /**
23281      * Returns true if the shadow is visible, else false
23282      */
23283     isVisible : function(){
23284         return this.el ? true : false;  
23285     },
23286
23287     /**
23288      * Direct alignment when values are already available. Show must be called at least once before
23289      * calling this method to ensure it is initialized.
23290      * @param {Number} left The target element left position
23291      * @param {Number} top The target element top position
23292      * @param {Number} width The target element width
23293      * @param {Number} height The target element height
23294      */
23295     realign : function(l, t, w, h){
23296         if(!this.el){
23297             return;
23298         }
23299         var a = this.adjusts, d = this.el.dom, s = d.style;
23300         var iea = 0;
23301         s.left = (l+a.l)+"px";
23302         s.top = (t+a.t)+"px";
23303         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
23304  
23305         if(s.width != sws || s.height != shs){
23306             s.width = sws;
23307             s.height = shs;
23308             if(!Roo.isIE){
23309                 var cn = d.childNodes;
23310                 var sww = Math.max(0, (sw-12))+"px";
23311                 cn[0].childNodes[1].style.width = sww;
23312                 cn[1].childNodes[1].style.width = sww;
23313                 cn[2].childNodes[1].style.width = sww;
23314                 cn[1].style.height = Math.max(0, (sh-12))+"px";
23315             }
23316         }
23317     },
23318
23319     /**
23320      * Hides this shadow
23321      */
23322     hide : function(){
23323         if(this.el){
23324             this.el.dom.style.display = "none";
23325             Roo.Shadow.Pool.push(this.el);
23326             delete this.el;
23327         }
23328     },
23329
23330     /**
23331      * Adjust the z-index of this shadow
23332      * @param {Number} zindex The new z-index
23333      */
23334     setZIndex : function(z){
23335         this.zIndex = z;
23336         if(this.el){
23337             this.el.setStyle("z-index", z);
23338         }
23339     }
23340 };
23341
23342 // Private utility class that manages the internal Shadow cache
23343 Roo.Shadow.Pool = function(){
23344     var p = [];
23345     var markup = Roo.isIE ?
23346                  '<div class="x-ie-shadow"></div>' :
23347                  '<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>';
23348     return {
23349         pull : function(){
23350             var sh = p.shift();
23351             if(!sh){
23352                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
23353                 sh.autoBoxAdjust = false;
23354             }
23355             return sh;
23356         },
23357
23358         push : function(sh){
23359             p.push(sh);
23360         }
23361     };
23362 }();/*
23363  * Based on:
23364  * Ext JS Library 1.1.1
23365  * Copyright(c) 2006-2007, Ext JS, LLC.
23366  *
23367  * Originally Released Under LGPL - original licence link has changed is not relivant.
23368  *
23369  * Fork - LGPL
23370  * <script type="text/javascript">
23371  */
23372
23373 /**
23374  * @class Roo.BoxComponent
23375  * @extends Roo.Component
23376  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
23377  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
23378  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
23379  * layout containers.
23380  * @constructor
23381  * @param {Roo.Element/String/Object} config The configuration options.
23382  */
23383 Roo.BoxComponent = function(config){
23384     Roo.Component.call(this, config);
23385     this.addEvents({
23386         /**
23387          * @event resize
23388          * Fires after the component is resized.
23389              * @param {Roo.Component} this
23390              * @param {Number} adjWidth The box-adjusted width that was set
23391              * @param {Number} adjHeight The box-adjusted height that was set
23392              * @param {Number} rawWidth The width that was originally specified
23393              * @param {Number} rawHeight The height that was originally specified
23394              */
23395         resize : true,
23396         /**
23397          * @event move
23398          * Fires after the component is moved.
23399              * @param {Roo.Component} this
23400              * @param {Number} x The new x position
23401              * @param {Number} y The new y position
23402              */
23403         move : true
23404     });
23405 };
23406
23407 Roo.extend(Roo.BoxComponent, Roo.Component, {
23408     // private, set in afterRender to signify that the component has been rendered
23409     boxReady : false,
23410     // private, used to defer height settings to subclasses
23411     deferHeight: false,
23412     /** @cfg {Number} width
23413      * width (optional) size of component
23414      */
23415      /** @cfg {Number} height
23416      * height (optional) size of component
23417      */
23418      
23419     /**
23420      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
23421      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
23422      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
23423      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
23424      * @return {Roo.BoxComponent} this
23425      */
23426     setSize : function(w, h){
23427         // support for standard size objects
23428         if(typeof w == 'object'){
23429             h = w.height;
23430             w = w.width;
23431         }
23432         // not rendered
23433         if(!this.boxReady){
23434             this.width = w;
23435             this.height = h;
23436             return this;
23437         }
23438
23439         // prevent recalcs when not needed
23440         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
23441             return this;
23442         }
23443         this.lastSize = {width: w, height: h};
23444
23445         var adj = this.adjustSize(w, h);
23446         var aw = adj.width, ah = adj.height;
23447         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
23448             var rz = this.getResizeEl();
23449             if(!this.deferHeight && aw !== undefined && ah !== undefined){
23450                 rz.setSize(aw, ah);
23451             }else if(!this.deferHeight && ah !== undefined){
23452                 rz.setHeight(ah);
23453             }else if(aw !== undefined){
23454                 rz.setWidth(aw);
23455             }
23456             this.onResize(aw, ah, w, h);
23457             this.fireEvent('resize', this, aw, ah, w, h);
23458         }
23459         return this;
23460     },
23461
23462     /**
23463      * Gets the current size of the component's underlying element.
23464      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
23465      */
23466     getSize : function(){
23467         return this.el.getSize();
23468     },
23469
23470     /**
23471      * Gets the current XY position of the component's underlying element.
23472      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23473      * @return {Array} The XY position of the element (e.g., [100, 200])
23474      */
23475     getPosition : function(local){
23476         if(local === true){
23477             return [this.el.getLeft(true), this.el.getTop(true)];
23478         }
23479         return this.xy || this.el.getXY();
23480     },
23481
23482     /**
23483      * Gets the current box measurements of the component's underlying element.
23484      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23485      * @returns {Object} box An object in the format {x, y, width, height}
23486      */
23487     getBox : function(local){
23488         var s = this.el.getSize();
23489         if(local){
23490             s.x = this.el.getLeft(true);
23491             s.y = this.el.getTop(true);
23492         }else{
23493             var xy = this.xy || this.el.getXY();
23494             s.x = xy[0];
23495             s.y = xy[1];
23496         }
23497         return s;
23498     },
23499
23500     /**
23501      * Sets the current box measurements of the component's underlying element.
23502      * @param {Object} box An object in the format {x, y, width, height}
23503      * @returns {Roo.BoxComponent} this
23504      */
23505     updateBox : function(box){
23506         this.setSize(box.width, box.height);
23507         this.setPagePosition(box.x, box.y);
23508         return this;
23509     },
23510
23511     // protected
23512     getResizeEl : function(){
23513         return this.resizeEl || this.el;
23514     },
23515
23516     // protected
23517     getPositionEl : function(){
23518         return this.positionEl || this.el;
23519     },
23520
23521     /**
23522      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23523      * This method fires the move event.
23524      * @param {Number} left The new left
23525      * @param {Number} top The new top
23526      * @returns {Roo.BoxComponent} this
23527      */
23528     setPosition : function(x, y){
23529         this.x = x;
23530         this.y = y;
23531         if(!this.boxReady){
23532             return this;
23533         }
23534         var adj = this.adjustPosition(x, y);
23535         var ax = adj.x, ay = adj.y;
23536
23537         var el = this.getPositionEl();
23538         if(ax !== undefined || ay !== undefined){
23539             if(ax !== undefined && ay !== undefined){
23540                 el.setLeftTop(ax, ay);
23541             }else if(ax !== undefined){
23542                 el.setLeft(ax);
23543             }else if(ay !== undefined){
23544                 el.setTop(ay);
23545             }
23546             this.onPosition(ax, ay);
23547             this.fireEvent('move', this, ax, ay);
23548         }
23549         return this;
23550     },
23551
23552     /**
23553      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23554      * This method fires the move event.
23555      * @param {Number} x The new x position
23556      * @param {Number} y The new y position
23557      * @returns {Roo.BoxComponent} this
23558      */
23559     setPagePosition : function(x, y){
23560         this.pageX = x;
23561         this.pageY = y;
23562         if(!this.boxReady){
23563             return;
23564         }
23565         if(x === undefined || y === undefined){ // cannot translate undefined points
23566             return;
23567         }
23568         var p = this.el.translatePoints(x, y);
23569         this.setPosition(p.left, p.top);
23570         return this;
23571     },
23572
23573     // private
23574     onRender : function(ct, position){
23575         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23576         if(this.resizeEl){
23577             this.resizeEl = Roo.get(this.resizeEl);
23578         }
23579         if(this.positionEl){
23580             this.positionEl = Roo.get(this.positionEl);
23581         }
23582     },
23583
23584     // private
23585     afterRender : function(){
23586         Roo.BoxComponent.superclass.afterRender.call(this);
23587         this.boxReady = true;
23588         this.setSize(this.width, this.height);
23589         if(this.x || this.y){
23590             this.setPosition(this.x, this.y);
23591         }
23592         if(this.pageX || this.pageY){
23593             this.setPagePosition(this.pageX, this.pageY);
23594         }
23595     },
23596
23597     /**
23598      * Force the component's size to recalculate based on the underlying element's current height and width.
23599      * @returns {Roo.BoxComponent} this
23600      */
23601     syncSize : function(){
23602         delete this.lastSize;
23603         this.setSize(this.el.getWidth(), this.el.getHeight());
23604         return this;
23605     },
23606
23607     /**
23608      * Called after the component is resized, this method is empty by default but can be implemented by any
23609      * subclass that needs to perform custom logic after a resize occurs.
23610      * @param {Number} adjWidth The box-adjusted width that was set
23611      * @param {Number} adjHeight The box-adjusted height that was set
23612      * @param {Number} rawWidth The width that was originally specified
23613      * @param {Number} rawHeight The height that was originally specified
23614      */
23615     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23616
23617     },
23618
23619     /**
23620      * Called after the component is moved, this method is empty by default but can be implemented by any
23621      * subclass that needs to perform custom logic after a move occurs.
23622      * @param {Number} x The new x position
23623      * @param {Number} y The new y position
23624      */
23625     onPosition : function(x, y){
23626
23627     },
23628
23629     // private
23630     adjustSize : function(w, h){
23631         if(this.autoWidth){
23632             w = 'auto';
23633         }
23634         if(this.autoHeight){
23635             h = 'auto';
23636         }
23637         return {width : w, height: h};
23638     },
23639
23640     // private
23641     adjustPosition : function(x, y){
23642         return {x : x, y: y};
23643     }
23644 });/*
23645  * Based on:
23646  * Ext JS Library 1.1.1
23647  * Copyright(c) 2006-2007, Ext JS, LLC.
23648  *
23649  * Originally Released Under LGPL - original licence link has changed is not relivant.
23650  *
23651  * Fork - LGPL
23652  * <script type="text/javascript">
23653  */
23654
23655
23656 /**
23657  * @class Roo.SplitBar
23658  * @extends Roo.util.Observable
23659  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23660  * <br><br>
23661  * Usage:
23662  * <pre><code>
23663 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23664                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23665 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23666 split.minSize = 100;
23667 split.maxSize = 600;
23668 split.animate = true;
23669 split.on('moved', splitterMoved);
23670 </code></pre>
23671  * @constructor
23672  * Create a new SplitBar
23673  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23674  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23675  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23676  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23677                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23678                         position of the SplitBar).
23679  */
23680 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23681     
23682     /** @private */
23683     this.el = Roo.get(dragElement, true);
23684     this.el.dom.unselectable = "on";
23685     /** @private */
23686     this.resizingEl = Roo.get(resizingElement, true);
23687
23688     /**
23689      * @private
23690      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23691      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23692      * @type Number
23693      */
23694     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23695     
23696     /**
23697      * The minimum size of the resizing element. (Defaults to 0)
23698      * @type Number
23699      */
23700     this.minSize = 0;
23701     
23702     /**
23703      * The maximum size of the resizing element. (Defaults to 2000)
23704      * @type Number
23705      */
23706     this.maxSize = 2000;
23707     
23708     /**
23709      * Whether to animate the transition to the new size
23710      * @type Boolean
23711      */
23712     this.animate = false;
23713     
23714     /**
23715      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23716      * @type Boolean
23717      */
23718     this.useShim = false;
23719     
23720     /** @private */
23721     this.shim = null;
23722     
23723     if(!existingProxy){
23724         /** @private */
23725         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23726     }else{
23727         this.proxy = Roo.get(existingProxy).dom;
23728     }
23729     /** @private */
23730     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23731     
23732     /** @private */
23733     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23734     
23735     /** @private */
23736     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23737     
23738     /** @private */
23739     this.dragSpecs = {};
23740     
23741     /**
23742      * @private The adapter to use to positon and resize elements
23743      */
23744     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23745     this.adapter.init(this);
23746     
23747     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23748         /** @private */
23749         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23750         this.el.addClass("x-splitbar-h");
23751     }else{
23752         /** @private */
23753         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23754         this.el.addClass("x-splitbar-v");
23755     }
23756     
23757     this.addEvents({
23758         /**
23759          * @event resize
23760          * Fires when the splitter is moved (alias for {@link #event-moved})
23761          * @param {Roo.SplitBar} this
23762          * @param {Number} newSize the new width or height
23763          */
23764         "resize" : true,
23765         /**
23766          * @event moved
23767          * Fires when the splitter is moved
23768          * @param {Roo.SplitBar} this
23769          * @param {Number} newSize the new width or height
23770          */
23771         "moved" : true,
23772         /**
23773          * @event beforeresize
23774          * Fires before the splitter is dragged
23775          * @param {Roo.SplitBar} this
23776          */
23777         "beforeresize" : true,
23778
23779         "beforeapply" : true
23780     });
23781
23782     Roo.util.Observable.call(this);
23783 };
23784
23785 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23786     onStartProxyDrag : function(x, y){
23787         this.fireEvent("beforeresize", this);
23788         if(!this.overlay){
23789             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23790             o.unselectable();
23791             o.enableDisplayMode("block");
23792             // all splitbars share the same overlay
23793             Roo.SplitBar.prototype.overlay = o;
23794         }
23795         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23796         this.overlay.show();
23797         Roo.get(this.proxy).setDisplayed("block");
23798         var size = this.adapter.getElementSize(this);
23799         this.activeMinSize = this.getMinimumSize();;
23800         this.activeMaxSize = this.getMaximumSize();;
23801         var c1 = size - this.activeMinSize;
23802         var c2 = Math.max(this.activeMaxSize - size, 0);
23803         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23804             this.dd.resetConstraints();
23805             this.dd.setXConstraint(
23806                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23807                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23808             );
23809             this.dd.setYConstraint(0, 0);
23810         }else{
23811             this.dd.resetConstraints();
23812             this.dd.setXConstraint(0, 0);
23813             this.dd.setYConstraint(
23814                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23815                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23816             );
23817          }
23818         this.dragSpecs.startSize = size;
23819         this.dragSpecs.startPoint = [x, y];
23820         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23821     },
23822     
23823     /** 
23824      * @private Called after the drag operation by the DDProxy
23825      */
23826     onEndProxyDrag : function(e){
23827         Roo.get(this.proxy).setDisplayed(false);
23828         var endPoint = Roo.lib.Event.getXY(e);
23829         if(this.overlay){
23830             this.overlay.hide();
23831         }
23832         var newSize;
23833         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23834             newSize = this.dragSpecs.startSize + 
23835                 (this.placement == Roo.SplitBar.LEFT ?
23836                     endPoint[0] - this.dragSpecs.startPoint[0] :
23837                     this.dragSpecs.startPoint[0] - endPoint[0]
23838                 );
23839         }else{
23840             newSize = this.dragSpecs.startSize + 
23841                 (this.placement == Roo.SplitBar.TOP ?
23842                     endPoint[1] - this.dragSpecs.startPoint[1] :
23843                     this.dragSpecs.startPoint[1] - endPoint[1]
23844                 );
23845         }
23846         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23847         if(newSize != this.dragSpecs.startSize){
23848             if(this.fireEvent('beforeapply', this, newSize) !== false){
23849                 this.adapter.setElementSize(this, newSize);
23850                 this.fireEvent("moved", this, newSize);
23851                 this.fireEvent("resize", this, newSize);
23852             }
23853         }
23854     },
23855     
23856     /**
23857      * Get the adapter this SplitBar uses
23858      * @return The adapter object
23859      */
23860     getAdapter : function(){
23861         return this.adapter;
23862     },
23863     
23864     /**
23865      * Set the adapter this SplitBar uses
23866      * @param {Object} adapter A SplitBar adapter object
23867      */
23868     setAdapter : function(adapter){
23869         this.adapter = adapter;
23870         this.adapter.init(this);
23871     },
23872     
23873     /**
23874      * Gets the minimum size for the resizing element
23875      * @return {Number} The minimum size
23876      */
23877     getMinimumSize : function(){
23878         return this.minSize;
23879     },
23880     
23881     /**
23882      * Sets the minimum size for the resizing element
23883      * @param {Number} minSize The minimum size
23884      */
23885     setMinimumSize : function(minSize){
23886         this.minSize = minSize;
23887     },
23888     
23889     /**
23890      * Gets the maximum size for the resizing element
23891      * @return {Number} The maximum size
23892      */
23893     getMaximumSize : function(){
23894         return this.maxSize;
23895     },
23896     
23897     /**
23898      * Sets the maximum size for the resizing element
23899      * @param {Number} maxSize The maximum size
23900      */
23901     setMaximumSize : function(maxSize){
23902         this.maxSize = maxSize;
23903     },
23904     
23905     /**
23906      * Sets the initialize size for the resizing element
23907      * @param {Number} size The initial size
23908      */
23909     setCurrentSize : function(size){
23910         var oldAnimate = this.animate;
23911         this.animate = false;
23912         this.adapter.setElementSize(this, size);
23913         this.animate = oldAnimate;
23914     },
23915     
23916     /**
23917      * Destroy this splitbar. 
23918      * @param {Boolean} removeEl True to remove the element
23919      */
23920     destroy : function(removeEl){
23921         if(this.shim){
23922             this.shim.remove();
23923         }
23924         this.dd.unreg();
23925         this.proxy.parentNode.removeChild(this.proxy);
23926         if(removeEl){
23927             this.el.remove();
23928         }
23929     }
23930 });
23931
23932 /**
23933  * @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.
23934  */
23935 Roo.SplitBar.createProxy = function(dir){
23936     var proxy = new Roo.Element(document.createElement("div"));
23937     proxy.unselectable();
23938     var cls = 'x-splitbar-proxy';
23939     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23940     document.body.appendChild(proxy.dom);
23941     return proxy.dom;
23942 };
23943
23944 /** 
23945  * @class Roo.SplitBar.BasicLayoutAdapter
23946  * Default Adapter. It assumes the splitter and resizing element are not positioned
23947  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23948  */
23949 Roo.SplitBar.BasicLayoutAdapter = function(){
23950 };
23951
23952 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23953     // do nothing for now
23954     init : function(s){
23955     
23956     },
23957     /**
23958      * Called before drag operations to get the current size of the resizing element. 
23959      * @param {Roo.SplitBar} s The SplitBar using this adapter
23960      */
23961      getElementSize : function(s){
23962         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23963             return s.resizingEl.getWidth();
23964         }else{
23965             return s.resizingEl.getHeight();
23966         }
23967     },
23968     
23969     /**
23970      * Called after drag operations to set the size of the resizing element.
23971      * @param {Roo.SplitBar} s The SplitBar using this adapter
23972      * @param {Number} newSize The new size to set
23973      * @param {Function} onComplete A function to be invoked when resizing is complete
23974      */
23975     setElementSize : function(s, newSize, onComplete){
23976         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23977             if(!s.animate){
23978                 s.resizingEl.setWidth(newSize);
23979                 if(onComplete){
23980                     onComplete(s, newSize);
23981                 }
23982             }else{
23983                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23984             }
23985         }else{
23986             
23987             if(!s.animate){
23988                 s.resizingEl.setHeight(newSize);
23989                 if(onComplete){
23990                     onComplete(s, newSize);
23991                 }
23992             }else{
23993                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23994             }
23995         }
23996     }
23997 };
23998
23999 /** 
24000  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24001  * @extends Roo.SplitBar.BasicLayoutAdapter
24002  * Adapter that  moves the splitter element to align with the resized sizing element. 
24003  * Used with an absolute positioned SplitBar.
24004  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24005  * document.body, make sure you assign an id to the body element.
24006  */
24007 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24008     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24009     this.container = Roo.get(container);
24010 };
24011
24012 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24013     init : function(s){
24014         this.basic.init(s);
24015     },
24016     
24017     getElementSize : function(s){
24018         return this.basic.getElementSize(s);
24019     },
24020     
24021     setElementSize : function(s, newSize, onComplete){
24022         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24023     },
24024     
24025     moveSplitter : function(s){
24026         var yes = Roo.SplitBar;
24027         switch(s.placement){
24028             case yes.LEFT:
24029                 s.el.setX(s.resizingEl.getRight());
24030                 break;
24031             case yes.RIGHT:
24032                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24033                 break;
24034             case yes.TOP:
24035                 s.el.setY(s.resizingEl.getBottom());
24036                 break;
24037             case yes.BOTTOM:
24038                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24039                 break;
24040         }
24041     }
24042 };
24043
24044 /**
24045  * Orientation constant - Create a vertical SplitBar
24046  * @static
24047  * @type Number
24048  */
24049 Roo.SplitBar.VERTICAL = 1;
24050
24051 /**
24052  * Orientation constant - Create a horizontal SplitBar
24053  * @static
24054  * @type Number
24055  */
24056 Roo.SplitBar.HORIZONTAL = 2;
24057
24058 /**
24059  * Placement constant - The resizing element is to the left of the splitter element
24060  * @static
24061  * @type Number
24062  */
24063 Roo.SplitBar.LEFT = 1;
24064
24065 /**
24066  * Placement constant - The resizing element is to the right of the splitter element
24067  * @static
24068  * @type Number
24069  */
24070 Roo.SplitBar.RIGHT = 2;
24071
24072 /**
24073  * Placement constant - The resizing element is positioned above the splitter element
24074  * @static
24075  * @type Number
24076  */
24077 Roo.SplitBar.TOP = 3;
24078
24079 /**
24080  * Placement constant - The resizing element is positioned under splitter element
24081  * @static
24082  * @type Number
24083  */
24084 Roo.SplitBar.BOTTOM = 4;
24085 /*
24086  * Based on:
24087  * Ext JS Library 1.1.1
24088  * Copyright(c) 2006-2007, Ext JS, LLC.
24089  *
24090  * Originally Released Under LGPL - original licence link has changed is not relivant.
24091  *
24092  * Fork - LGPL
24093  * <script type="text/javascript">
24094  */
24095
24096 /**
24097  * @class Roo.View
24098  * @extends Roo.util.Observable
24099  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24100  * This class also supports single and multi selection modes. <br>
24101  * Create a data model bound view:
24102  <pre><code>
24103  var store = new Roo.data.Store(...);
24104
24105  var view = new Roo.View({
24106     el : "my-element",
24107     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24108  
24109     singleSelect: true,
24110     selectedClass: "ydataview-selected",
24111     store: store
24112  });
24113
24114  // listen for node click?
24115  view.on("click", function(vw, index, node, e){
24116  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24117  });
24118
24119  // load XML data
24120  dataModel.load("foobar.xml");
24121  </code></pre>
24122  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24123  * <br><br>
24124  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24125  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24126  * 
24127  * Note: old style constructor is still suported (container, template, config)
24128  * 
24129  * @constructor
24130  * Create a new View
24131  * @param {Object} config The config object
24132  * 
24133  */
24134 Roo.View = function(config, depreciated_tpl, depreciated_config){
24135     
24136     if (typeof(depreciated_tpl) == 'undefined') {
24137         // new way.. - universal constructor.
24138         Roo.apply(this, config);
24139         this.el  = Roo.get(this.el);
24140     } else {
24141         // old format..
24142         this.el  = Roo.get(config);
24143         this.tpl = depreciated_tpl;
24144         Roo.apply(this, depreciated_config);
24145     }
24146      
24147     
24148     if(typeof(this.tpl) == "string"){
24149         this.tpl = new Roo.Template(this.tpl);
24150     } else {
24151         // support xtype ctors..
24152         this.tpl = new Roo.factory(this.tpl, Roo);
24153     }
24154     
24155     
24156     this.tpl.compile();
24157    
24158
24159      
24160     /** @private */
24161     this.addEvents({
24162         /**
24163          * @event beforeclick
24164          * Fires before a click is processed. Returns false to cancel the default action.
24165          * @param {Roo.View} this
24166          * @param {Number} index The index of the target node
24167          * @param {HTMLElement} node The target node
24168          * @param {Roo.EventObject} e The raw event object
24169          */
24170             "beforeclick" : true,
24171         /**
24172          * @event click
24173          * Fires when a template node is clicked.
24174          * @param {Roo.View} this
24175          * @param {Number} index The index of the target node
24176          * @param {HTMLElement} node The target node
24177          * @param {Roo.EventObject} e The raw event object
24178          */
24179             "click" : true,
24180         /**
24181          * @event dblclick
24182          * Fires when a template node is double clicked.
24183          * @param {Roo.View} this
24184          * @param {Number} index The index of the target node
24185          * @param {HTMLElement} node The target node
24186          * @param {Roo.EventObject} e The raw event object
24187          */
24188             "dblclick" : true,
24189         /**
24190          * @event contextmenu
24191          * Fires when a template node is right clicked.
24192          * @param {Roo.View} this
24193          * @param {Number} index The index of the target node
24194          * @param {HTMLElement} node The target node
24195          * @param {Roo.EventObject} e The raw event object
24196          */
24197             "contextmenu" : true,
24198         /**
24199          * @event selectionchange
24200          * Fires when the selected nodes change.
24201          * @param {Roo.View} this
24202          * @param {Array} selections Array of the selected nodes
24203          */
24204             "selectionchange" : true,
24205     
24206         /**
24207          * @event beforeselect
24208          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24209          * @param {Roo.View} this
24210          * @param {HTMLElement} node The node to be selected
24211          * @param {Array} selections Array of currently selected nodes
24212          */
24213             "beforeselect" : true,
24214         /**
24215          * @event preparedata
24216          * Fires on every row to render, to allow you to change the data.
24217          * @param {Roo.View} this
24218          * @param {Object} data to be rendered (change this)
24219          */
24220           "preparedata" : true
24221         });
24222
24223     this.el.on({
24224         "click": this.onClick,
24225         "dblclick": this.onDblClick,
24226         "contextmenu": this.onContextMenu,
24227         scope:this
24228     });
24229
24230     this.selections = [];
24231     this.nodes = [];
24232     this.cmp = new Roo.CompositeElementLite([]);
24233     if(this.store){
24234         this.store = Roo.factory(this.store, Roo.data);
24235         this.setStore(this.store, true);
24236     }
24237     Roo.View.superclass.constructor.call(this);
24238 };
24239
24240 Roo.extend(Roo.View, Roo.util.Observable, {
24241     
24242      /**
24243      * @cfg {Roo.data.Store} store Data store to load data from.
24244      */
24245     store : false,
24246     
24247     /**
24248      * @cfg {String|Roo.Element} el The container element.
24249      */
24250     el : '',
24251     
24252     /**
24253      * @cfg {String|Roo.Template} tpl The template used by this View 
24254      */
24255     tpl : false,
24256     /**
24257      * @cfg {String} dataName the named area of the template to use as the data area
24258      *                          Works with domtemplates roo-name="name"
24259      */
24260     dataName: false,
24261     /**
24262      * @cfg {String} selectedClass The css class to add to selected nodes
24263      */
24264     selectedClass : "x-view-selected",
24265      /**
24266      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24267      */
24268     emptyText : "",
24269     /**
24270      * @cfg {Boolean} multiSelect Allow multiple selection
24271      */
24272     multiSelect : false,
24273     /**
24274      * @cfg {Boolean} singleSelect Allow single selection
24275      */
24276     singleSelect:  false,
24277     
24278     /**
24279      * @cfg {Boolean} toggleSelect - selecting 
24280      */
24281     toggleSelect : false,
24282     
24283     /**
24284      * Returns the element this view is bound to.
24285      * @return {Roo.Element}
24286      */
24287     getEl : function(){
24288         return this.el;
24289     },
24290
24291     /**
24292      * Refreshes the view.
24293      */
24294     refresh : function(){
24295         var t = this.tpl;
24296         
24297         // if we are using something like 'domtemplate', then
24298         // the what gets used is:
24299         // t.applySubtemplate(NAME, data, wrapping data..)
24300         // the outer template then get' applied with
24301         //     the store 'extra data'
24302         // and the body get's added to the
24303         //      roo-name="data" node?
24304         //      <span class='roo-tpl-{name}'></span> ?????
24305         
24306         
24307         
24308         this.clearSelections();
24309         this.el.update("");
24310         var html = [];
24311         var records = this.store.getRange();
24312         if(records.length < 1) {
24313             
24314             // is this valid??  = should it render a template??
24315             
24316             this.el.update(this.emptyText);
24317             return;
24318         }
24319         var el = this.el;
24320         if (this.dataName) {
24321             this.el.update(t.apply(this.store.meta)); //????
24322             el = this.el.child('.roo-tpl-' + this.dataName);
24323         }
24324         
24325         for(var i = 0, len = records.length; i < len; i++){
24326             var data = this.prepareData(records[i].data, i, records[i]);
24327             this.fireEvent("preparedata", this, data, i, records[i]);
24328             html[html.length] = Roo.util.Format.trim(
24329                 this.dataName ?
24330                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24331                     t.apply(data)
24332             );
24333         }
24334         
24335         
24336         
24337         el.update(html.join(""));
24338         this.nodes = el.dom.childNodes;
24339         this.updateIndexes(0);
24340     },
24341
24342     /**
24343      * Function to override to reformat the data that is sent to
24344      * the template for each node.
24345      * DEPRICATED - use the preparedata event handler.
24346      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24347      * a JSON object for an UpdateManager bound view).
24348      */
24349     prepareData : function(data, index, record)
24350     {
24351         this.fireEvent("preparedata", this, data, index, record);
24352         return data;
24353     },
24354
24355     onUpdate : function(ds, record){
24356         this.clearSelections();
24357         var index = this.store.indexOf(record);
24358         var n = this.nodes[index];
24359         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24360         n.parentNode.removeChild(n);
24361         this.updateIndexes(index, index);
24362     },
24363
24364     
24365     
24366 // --------- FIXME     
24367     onAdd : function(ds, records, index)
24368     {
24369         this.clearSelections();
24370         if(this.nodes.length == 0){
24371             this.refresh();
24372             return;
24373         }
24374         var n = this.nodes[index];
24375         for(var i = 0, len = records.length; i < len; i++){
24376             var d = this.prepareData(records[i].data, i, records[i]);
24377             if(n){
24378                 this.tpl.insertBefore(n, d);
24379             }else{
24380                 
24381                 this.tpl.append(this.el, d);
24382             }
24383         }
24384         this.updateIndexes(index);
24385     },
24386
24387     onRemove : function(ds, record, index){
24388         this.clearSelections();
24389         var el = this.dataName  ?
24390             this.el.child('.roo-tpl-' + this.dataName) :
24391             this.el; 
24392         el.dom.removeChild(this.nodes[index]);
24393         this.updateIndexes(index);
24394     },
24395
24396     /**
24397      * Refresh an individual node.
24398      * @param {Number} index
24399      */
24400     refreshNode : function(index){
24401         this.onUpdate(this.store, this.store.getAt(index));
24402     },
24403
24404     updateIndexes : function(startIndex, endIndex){
24405         var ns = this.nodes;
24406         startIndex = startIndex || 0;
24407         endIndex = endIndex || ns.length - 1;
24408         for(var i = startIndex; i <= endIndex; i++){
24409             ns[i].nodeIndex = i;
24410         }
24411     },
24412
24413     /**
24414      * Changes the data store this view uses and refresh the view.
24415      * @param {Store} store
24416      */
24417     setStore : function(store, initial){
24418         if(!initial && this.store){
24419             this.store.un("datachanged", this.refresh);
24420             this.store.un("add", this.onAdd);
24421             this.store.un("remove", this.onRemove);
24422             this.store.un("update", this.onUpdate);
24423             this.store.un("clear", this.refresh);
24424         }
24425         if(store){
24426           
24427             store.on("datachanged", this.refresh, this);
24428             store.on("add", this.onAdd, this);
24429             store.on("remove", this.onRemove, this);
24430             store.on("update", this.onUpdate, this);
24431             store.on("clear", this.refresh, this);
24432         }
24433         
24434         if(store){
24435             this.refresh();
24436         }
24437     },
24438
24439     /**
24440      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
24441      * @param {HTMLElement} node
24442      * @return {HTMLElement} The template node
24443      */
24444     findItemFromChild : function(node){
24445         var el = this.dataName  ?
24446             this.el.child('.roo-tpl-' + this.dataName,true) :
24447             this.el.dom; 
24448         
24449         if(!node || node.parentNode == el){
24450                     return node;
24451             }
24452             var p = node.parentNode;
24453             while(p && p != el){
24454             if(p.parentNode == el){
24455                 return p;
24456             }
24457             p = p.parentNode;
24458         }
24459             return null;
24460     },
24461
24462     /** @ignore */
24463     onClick : function(e){
24464         var item = this.findItemFromChild(e.getTarget());
24465         if(item){
24466             var index = this.indexOf(item);
24467             if(this.onItemClick(item, index, e) !== false){
24468                 this.fireEvent("click", this, index, item, e);
24469             }
24470         }else{
24471             this.clearSelections();
24472         }
24473     },
24474
24475     /** @ignore */
24476     onContextMenu : function(e){
24477         var item = this.findItemFromChild(e.getTarget());
24478         if(item){
24479             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
24480         }
24481     },
24482
24483     /** @ignore */
24484     onDblClick : function(e){
24485         var item = this.findItemFromChild(e.getTarget());
24486         if(item){
24487             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
24488         }
24489     },
24490
24491     onItemClick : function(item, index, e)
24492     {
24493         if(this.fireEvent("beforeclick", this, index, item, e) === false){
24494             return false;
24495         }
24496         if (this.toggleSelect) {
24497             var m = this.isSelected(item) ? 'unselect' : 'select';
24498             Roo.log(m);
24499             var _t = this;
24500             _t[m](item, true, false);
24501             return true;
24502         }
24503         if(this.multiSelect || this.singleSelect){
24504             if(this.multiSelect && e.shiftKey && this.lastSelection){
24505                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
24506             }else{
24507                 this.select(item, this.multiSelect && e.ctrlKey);
24508                 this.lastSelection = item;
24509             }
24510             e.preventDefault();
24511         }
24512         return true;
24513     },
24514
24515     /**
24516      * Get the number of selected nodes.
24517      * @return {Number}
24518      */
24519     getSelectionCount : function(){
24520         return this.selections.length;
24521     },
24522
24523     /**
24524      * Get the currently selected nodes.
24525      * @return {Array} An array of HTMLElements
24526      */
24527     getSelectedNodes : function(){
24528         return this.selections;
24529     },
24530
24531     /**
24532      * Get the indexes of the selected nodes.
24533      * @return {Array}
24534      */
24535     getSelectedIndexes : function(){
24536         var indexes = [], s = this.selections;
24537         for(var i = 0, len = s.length; i < len; i++){
24538             indexes.push(s[i].nodeIndex);
24539         }
24540         return indexes;
24541     },
24542
24543     /**
24544      * Clear all selections
24545      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
24546      */
24547     clearSelections : function(suppressEvent){
24548         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24549             this.cmp.elements = this.selections;
24550             this.cmp.removeClass(this.selectedClass);
24551             this.selections = [];
24552             if(!suppressEvent){
24553                 this.fireEvent("selectionchange", this, this.selections);
24554             }
24555         }
24556     },
24557
24558     /**
24559      * Returns true if the passed node is selected
24560      * @param {HTMLElement/Number} node The node or node index
24561      * @return {Boolean}
24562      */
24563     isSelected : function(node){
24564         var s = this.selections;
24565         if(s.length < 1){
24566             return false;
24567         }
24568         node = this.getNode(node);
24569         return s.indexOf(node) !== -1;
24570     },
24571
24572     /**
24573      * Selects nodes.
24574      * @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
24575      * @param {Boolean} keepExisting (optional) true to keep existing selections
24576      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24577      */
24578     select : function(nodeInfo, keepExisting, suppressEvent){
24579         if(nodeInfo instanceof Array){
24580             if(!keepExisting){
24581                 this.clearSelections(true);
24582             }
24583             for(var i = 0, len = nodeInfo.length; i < len; i++){
24584                 this.select(nodeInfo[i], true, true);
24585             }
24586             return;
24587         } 
24588         var node = this.getNode(nodeInfo);
24589         if(!node || this.isSelected(node)){
24590             return; // already selected.
24591         }
24592         if(!keepExisting){
24593             this.clearSelections(true);
24594         }
24595         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24596             Roo.fly(node).addClass(this.selectedClass);
24597             this.selections.push(node);
24598             if(!suppressEvent){
24599                 this.fireEvent("selectionchange", this, this.selections);
24600             }
24601         }
24602         
24603         
24604     },
24605       /**
24606      * Unselects nodes.
24607      * @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
24608      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24609      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24610      */
24611     unselect : function(nodeInfo, keepExisting, suppressEvent)
24612     {
24613         if(nodeInfo instanceof Array){
24614             Roo.each(this.selections, function(s) {
24615                 this.unselect(s, nodeInfo);
24616             }, this);
24617             return;
24618         }
24619         var node = this.getNode(nodeInfo);
24620         if(!node || !this.isSelected(node)){
24621             Roo.log("not selected");
24622             return; // not selected.
24623         }
24624         // fireevent???
24625         var ns = [];
24626         Roo.each(this.selections, function(s) {
24627             if (s == node ) {
24628                 Roo.fly(node).removeClass(this.selectedClass);
24629
24630                 return;
24631             }
24632             ns.push(s);
24633         },this);
24634         
24635         this.selections= ns;
24636         this.fireEvent("selectionchange", this, this.selections);
24637     },
24638
24639     /**
24640      * Gets a template node.
24641      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24642      * @return {HTMLElement} The node or null if it wasn't found
24643      */
24644     getNode : function(nodeInfo){
24645         if(typeof nodeInfo == "string"){
24646             return document.getElementById(nodeInfo);
24647         }else if(typeof nodeInfo == "number"){
24648             return this.nodes[nodeInfo];
24649         }
24650         return nodeInfo;
24651     },
24652
24653     /**
24654      * Gets a range template nodes.
24655      * @param {Number} startIndex
24656      * @param {Number} endIndex
24657      * @return {Array} An array of nodes
24658      */
24659     getNodes : function(start, end){
24660         var ns = this.nodes;
24661         start = start || 0;
24662         end = typeof end == "undefined" ? ns.length - 1 : end;
24663         var nodes = [];
24664         if(start <= end){
24665             for(var i = start; i <= end; i++){
24666                 nodes.push(ns[i]);
24667             }
24668         } else{
24669             for(var i = start; i >= end; i--){
24670                 nodes.push(ns[i]);
24671             }
24672         }
24673         return nodes;
24674     },
24675
24676     /**
24677      * Finds the index of the passed node
24678      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24679      * @return {Number} The index of the node or -1
24680      */
24681     indexOf : function(node){
24682         node = this.getNode(node);
24683         if(typeof node.nodeIndex == "number"){
24684             return node.nodeIndex;
24685         }
24686         var ns = this.nodes;
24687         for(var i = 0, len = ns.length; i < len; i++){
24688             if(ns[i] == node){
24689                 return i;
24690             }
24691         }
24692         return -1;
24693     }
24694 });
24695 /*
24696  * Based on:
24697  * Ext JS Library 1.1.1
24698  * Copyright(c) 2006-2007, Ext JS, LLC.
24699  *
24700  * Originally Released Under LGPL - original licence link has changed is not relivant.
24701  *
24702  * Fork - LGPL
24703  * <script type="text/javascript">
24704  */
24705
24706 /**
24707  * @class Roo.JsonView
24708  * @extends Roo.View
24709  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24710 <pre><code>
24711 var view = new Roo.JsonView({
24712     container: "my-element",
24713     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24714     multiSelect: true, 
24715     jsonRoot: "data" 
24716 });
24717
24718 // listen for node click?
24719 view.on("click", function(vw, index, node, e){
24720     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24721 });
24722
24723 // direct load of JSON data
24724 view.load("foobar.php");
24725
24726 // Example from my blog list
24727 var tpl = new Roo.Template(
24728     '&lt;div class="entry"&gt;' +
24729     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24730     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24731     "&lt;/div&gt;&lt;hr /&gt;"
24732 );
24733
24734 var moreView = new Roo.JsonView({
24735     container :  "entry-list", 
24736     template : tpl,
24737     jsonRoot: "posts"
24738 });
24739 moreView.on("beforerender", this.sortEntries, this);
24740 moreView.load({
24741     url: "/blog/get-posts.php",
24742     params: "allposts=true",
24743     text: "Loading Blog Entries..."
24744 });
24745 </code></pre>
24746
24747 * Note: old code is supported with arguments : (container, template, config)
24748
24749
24750  * @constructor
24751  * Create a new JsonView
24752  * 
24753  * @param {Object} config The config object
24754  * 
24755  */
24756 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24757     
24758     
24759     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24760
24761     var um = this.el.getUpdateManager();
24762     um.setRenderer(this);
24763     um.on("update", this.onLoad, this);
24764     um.on("failure", this.onLoadException, this);
24765
24766     /**
24767      * @event beforerender
24768      * Fires before rendering of the downloaded JSON data.
24769      * @param {Roo.JsonView} this
24770      * @param {Object} data The JSON data loaded
24771      */
24772     /**
24773      * @event load
24774      * Fires when data is loaded.
24775      * @param {Roo.JsonView} this
24776      * @param {Object} data The JSON data loaded
24777      * @param {Object} response The raw Connect response object
24778      */
24779     /**
24780      * @event loadexception
24781      * Fires when loading fails.
24782      * @param {Roo.JsonView} this
24783      * @param {Object} response The raw Connect response object
24784      */
24785     this.addEvents({
24786         'beforerender' : true,
24787         'load' : true,
24788         'loadexception' : true
24789     });
24790 };
24791 Roo.extend(Roo.JsonView, Roo.View, {
24792     /**
24793      * @type {String} The root property in the loaded JSON object that contains the data
24794      */
24795     jsonRoot : "",
24796
24797     /**
24798      * Refreshes the view.
24799      */
24800     refresh : function(){
24801         this.clearSelections();
24802         this.el.update("");
24803         var html = [];
24804         var o = this.jsonData;
24805         if(o && o.length > 0){
24806             for(var i = 0, len = o.length; i < len; i++){
24807                 var data = this.prepareData(o[i], i, o);
24808                 html[html.length] = this.tpl.apply(data);
24809             }
24810         }else{
24811             html.push(this.emptyText);
24812         }
24813         this.el.update(html.join(""));
24814         this.nodes = this.el.dom.childNodes;
24815         this.updateIndexes(0);
24816     },
24817
24818     /**
24819      * 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.
24820      * @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:
24821      <pre><code>
24822      view.load({
24823          url: "your-url.php",
24824          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24825          callback: yourFunction,
24826          scope: yourObject, //(optional scope)
24827          discardUrl: false,
24828          nocache: false,
24829          text: "Loading...",
24830          timeout: 30,
24831          scripts: false
24832      });
24833      </code></pre>
24834      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24835      * 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.
24836      * @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}
24837      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24838      * @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.
24839      */
24840     load : function(){
24841         var um = this.el.getUpdateManager();
24842         um.update.apply(um, arguments);
24843     },
24844
24845     render : function(el, response){
24846         this.clearSelections();
24847         this.el.update("");
24848         var o;
24849         try{
24850             o = Roo.util.JSON.decode(response.responseText);
24851             if(this.jsonRoot){
24852                 
24853                 o = o[this.jsonRoot];
24854             }
24855         } catch(e){
24856         }
24857         /**
24858          * The current JSON data or null
24859          */
24860         this.jsonData = o;
24861         this.beforeRender();
24862         this.refresh();
24863     },
24864
24865 /**
24866  * Get the number of records in the current JSON dataset
24867  * @return {Number}
24868  */
24869     getCount : function(){
24870         return this.jsonData ? this.jsonData.length : 0;
24871     },
24872
24873 /**
24874  * Returns the JSON object for the specified node(s)
24875  * @param {HTMLElement/Array} node The node or an array of nodes
24876  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24877  * you get the JSON object for the node
24878  */
24879     getNodeData : function(node){
24880         if(node instanceof Array){
24881             var data = [];
24882             for(var i = 0, len = node.length; i < len; i++){
24883                 data.push(this.getNodeData(node[i]));
24884             }
24885             return data;
24886         }
24887         return this.jsonData[this.indexOf(node)] || null;
24888     },
24889
24890     beforeRender : function(){
24891         this.snapshot = this.jsonData;
24892         if(this.sortInfo){
24893             this.sort.apply(this, this.sortInfo);
24894         }
24895         this.fireEvent("beforerender", this, this.jsonData);
24896     },
24897
24898     onLoad : function(el, o){
24899         this.fireEvent("load", this, this.jsonData, o);
24900     },
24901
24902     onLoadException : function(el, o){
24903         this.fireEvent("loadexception", this, o);
24904     },
24905
24906 /**
24907  * Filter the data by a specific property.
24908  * @param {String} property A property on your JSON objects
24909  * @param {String/RegExp} value Either string that the property values
24910  * should start with, or a RegExp to test against the property
24911  */
24912     filter : function(property, value){
24913         if(this.jsonData){
24914             var data = [];
24915             var ss = this.snapshot;
24916             if(typeof value == "string"){
24917                 var vlen = value.length;
24918                 if(vlen == 0){
24919                     this.clearFilter();
24920                     return;
24921                 }
24922                 value = value.toLowerCase();
24923                 for(var i = 0, len = ss.length; i < len; i++){
24924                     var o = ss[i];
24925                     if(o[property].substr(0, vlen).toLowerCase() == value){
24926                         data.push(o);
24927                     }
24928                 }
24929             } else if(value.exec){ // regex?
24930                 for(var i = 0, len = ss.length; i < len; i++){
24931                     var o = ss[i];
24932                     if(value.test(o[property])){
24933                         data.push(o);
24934                     }
24935                 }
24936             } else{
24937                 return;
24938             }
24939             this.jsonData = data;
24940             this.refresh();
24941         }
24942     },
24943
24944 /**
24945  * Filter by a function. The passed function will be called with each
24946  * object in the current dataset. If the function returns true the value is kept,
24947  * otherwise it is filtered.
24948  * @param {Function} fn
24949  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24950  */
24951     filterBy : function(fn, scope){
24952         if(this.jsonData){
24953             var data = [];
24954             var ss = this.snapshot;
24955             for(var i = 0, len = ss.length; i < len; i++){
24956                 var o = ss[i];
24957                 if(fn.call(scope || this, o)){
24958                     data.push(o);
24959                 }
24960             }
24961             this.jsonData = data;
24962             this.refresh();
24963         }
24964     },
24965
24966 /**
24967  * Clears the current filter.
24968  */
24969     clearFilter : function(){
24970         if(this.snapshot && this.jsonData != this.snapshot){
24971             this.jsonData = this.snapshot;
24972             this.refresh();
24973         }
24974     },
24975
24976
24977 /**
24978  * Sorts the data for this view and refreshes it.
24979  * @param {String} property A property on your JSON objects to sort on
24980  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24981  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24982  */
24983     sort : function(property, dir, sortType){
24984         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24985         if(this.jsonData){
24986             var p = property;
24987             var dsc = dir && dir.toLowerCase() == "desc";
24988             var f = function(o1, o2){
24989                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24990                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24991                 ;
24992                 if(v1 < v2){
24993                     return dsc ? +1 : -1;
24994                 } else if(v1 > v2){
24995                     return dsc ? -1 : +1;
24996                 } else{
24997                     return 0;
24998                 }
24999             };
25000             this.jsonData.sort(f);
25001             this.refresh();
25002             if(this.jsonData != this.snapshot){
25003                 this.snapshot.sort(f);
25004             }
25005         }
25006     }
25007 });/*
25008  * Based on:
25009  * Ext JS Library 1.1.1
25010  * Copyright(c) 2006-2007, Ext JS, LLC.
25011  *
25012  * Originally Released Under LGPL - original licence link has changed is not relivant.
25013  *
25014  * Fork - LGPL
25015  * <script type="text/javascript">
25016  */
25017  
25018
25019 /**
25020  * @class Roo.ColorPalette
25021  * @extends Roo.Component
25022  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25023  * Here's an example of typical usage:
25024  * <pre><code>
25025 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25026 cp.render('my-div');
25027
25028 cp.on('select', function(palette, selColor){
25029     // do something with selColor
25030 });
25031 </code></pre>
25032  * @constructor
25033  * Create a new ColorPalette
25034  * @param {Object} config The config object
25035  */
25036 Roo.ColorPalette = function(config){
25037     Roo.ColorPalette.superclass.constructor.call(this, config);
25038     this.addEvents({
25039         /**
25040              * @event select
25041              * Fires when a color is selected
25042              * @param {ColorPalette} this
25043              * @param {String} color The 6-digit color hex code (without the # symbol)
25044              */
25045         select: true
25046     });
25047
25048     if(this.handler){
25049         this.on("select", this.handler, this.scope, true);
25050     }
25051 };
25052 Roo.extend(Roo.ColorPalette, Roo.Component, {
25053     /**
25054      * @cfg {String} itemCls
25055      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25056      */
25057     itemCls : "x-color-palette",
25058     /**
25059      * @cfg {String} value
25060      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25061      * the hex codes are case-sensitive.
25062      */
25063     value : null,
25064     clickEvent:'click',
25065     // private
25066     ctype: "Roo.ColorPalette",
25067
25068     /**
25069      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25070      */
25071     allowReselect : false,
25072
25073     /**
25074      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25075      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25076      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25077      * of colors with the width setting until the box is symmetrical.</p>
25078      * <p>You can override individual colors if needed:</p>
25079      * <pre><code>
25080 var cp = new Roo.ColorPalette();
25081 cp.colors[0] = "FF0000";  // change the first box to red
25082 </code></pre>
25083
25084 Or you can provide a custom array of your own for complete control:
25085 <pre><code>
25086 var cp = new Roo.ColorPalette();
25087 cp.colors = ["000000", "993300", "333300"];
25088 </code></pre>
25089      * @type Array
25090      */
25091     colors : [
25092         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25093         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25094         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25095         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25096         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25097     ],
25098
25099     // private
25100     onRender : function(container, position){
25101         var t = new Roo.MasterTemplate(
25102             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25103         );
25104         var c = this.colors;
25105         for(var i = 0, len = c.length; i < len; i++){
25106             t.add([c[i]]);
25107         }
25108         var el = document.createElement("div");
25109         el.className = this.itemCls;
25110         t.overwrite(el);
25111         container.dom.insertBefore(el, position);
25112         this.el = Roo.get(el);
25113         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25114         if(this.clickEvent != 'click'){
25115             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25116         }
25117     },
25118
25119     // private
25120     afterRender : function(){
25121         Roo.ColorPalette.superclass.afterRender.call(this);
25122         if(this.value){
25123             var s = this.value;
25124             this.value = null;
25125             this.select(s);
25126         }
25127     },
25128
25129     // private
25130     handleClick : function(e, t){
25131         e.preventDefault();
25132         if(!this.disabled){
25133             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25134             this.select(c.toUpperCase());
25135         }
25136     },
25137
25138     /**
25139      * Selects the specified color in the palette (fires the select event)
25140      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25141      */
25142     select : function(color){
25143         color = color.replace("#", "");
25144         if(color != this.value || this.allowReselect){
25145             var el = this.el;
25146             if(this.value){
25147                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25148             }
25149             el.child("a.color-"+color).addClass("x-color-palette-sel");
25150             this.value = color;
25151             this.fireEvent("select", this, color);
25152         }
25153     }
25154 });/*
25155  * Based on:
25156  * Ext JS Library 1.1.1
25157  * Copyright(c) 2006-2007, Ext JS, LLC.
25158  *
25159  * Originally Released Under LGPL - original licence link has changed is not relivant.
25160  *
25161  * Fork - LGPL
25162  * <script type="text/javascript">
25163  */
25164  
25165 /**
25166  * @class Roo.DatePicker
25167  * @extends Roo.Component
25168  * Simple date picker class.
25169  * @constructor
25170  * Create a new DatePicker
25171  * @param {Object} config The config object
25172  */
25173 Roo.DatePicker = function(config){
25174     Roo.DatePicker.superclass.constructor.call(this, config);
25175
25176     this.value = config && config.value ?
25177                  config.value.clearTime() : new Date().clearTime();
25178
25179     this.addEvents({
25180         /**
25181              * @event select
25182              * Fires when a date is selected
25183              * @param {DatePicker} this
25184              * @param {Date} date The selected date
25185              */
25186         'select': true,
25187         /**
25188              * @event monthchange
25189              * Fires when the displayed month changes 
25190              * @param {DatePicker} this
25191              * @param {Date} date The selected month
25192              */
25193         'monthchange': true
25194     });
25195
25196     if(this.handler){
25197         this.on("select", this.handler,  this.scope || this);
25198     }
25199     // build the disabledDatesRE
25200     if(!this.disabledDatesRE && this.disabledDates){
25201         var dd = this.disabledDates;
25202         var re = "(?:";
25203         for(var i = 0; i < dd.length; i++){
25204             re += dd[i];
25205             if(i != dd.length-1) re += "|";
25206         }
25207         this.disabledDatesRE = new RegExp(re + ")");
25208     }
25209 };
25210
25211 Roo.extend(Roo.DatePicker, Roo.Component, {
25212     /**
25213      * @cfg {String} todayText
25214      * The text to display on the button that selects the current date (defaults to "Today")
25215      */
25216     todayText : "Today",
25217     /**
25218      * @cfg {String} okText
25219      * The text to display on the ok button
25220      */
25221     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25222     /**
25223      * @cfg {String} cancelText
25224      * The text to display on the cancel button
25225      */
25226     cancelText : "Cancel",
25227     /**
25228      * @cfg {String} todayTip
25229      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25230      */
25231     todayTip : "{0} (Spacebar)",
25232     /**
25233      * @cfg {Date} minDate
25234      * Minimum allowable date (JavaScript date object, defaults to null)
25235      */
25236     minDate : null,
25237     /**
25238      * @cfg {Date} maxDate
25239      * Maximum allowable date (JavaScript date object, defaults to null)
25240      */
25241     maxDate : null,
25242     /**
25243      * @cfg {String} minText
25244      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25245      */
25246     minText : "This date is before the minimum date",
25247     /**
25248      * @cfg {String} maxText
25249      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25250      */
25251     maxText : "This date is after the maximum date",
25252     /**
25253      * @cfg {String} format
25254      * The default date format string which can be overriden for localization support.  The format must be
25255      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25256      */
25257     format : "m/d/y",
25258     /**
25259      * @cfg {Array} disabledDays
25260      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25261      */
25262     disabledDays : null,
25263     /**
25264      * @cfg {String} disabledDaysText
25265      * The tooltip to display when the date falls on a disabled day (defaults to "")
25266      */
25267     disabledDaysText : "",
25268     /**
25269      * @cfg {RegExp} disabledDatesRE
25270      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25271      */
25272     disabledDatesRE : null,
25273     /**
25274      * @cfg {String} disabledDatesText
25275      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25276      */
25277     disabledDatesText : "",
25278     /**
25279      * @cfg {Boolean} constrainToViewport
25280      * True to constrain the date picker to the viewport (defaults to true)
25281      */
25282     constrainToViewport : true,
25283     /**
25284      * @cfg {Array} monthNames
25285      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25286      */
25287     monthNames : Date.monthNames,
25288     /**
25289      * @cfg {Array} dayNames
25290      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25291      */
25292     dayNames : Date.dayNames,
25293     /**
25294      * @cfg {String} nextText
25295      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25296      */
25297     nextText: 'Next Month (Control+Right)',
25298     /**
25299      * @cfg {String} prevText
25300      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25301      */
25302     prevText: 'Previous Month (Control+Left)',
25303     /**
25304      * @cfg {String} monthYearText
25305      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25306      */
25307     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25308     /**
25309      * @cfg {Number} startDay
25310      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25311      */
25312     startDay : 0,
25313     /**
25314      * @cfg {Bool} showClear
25315      * Show a clear button (usefull for date form elements that can be blank.)
25316      */
25317     
25318     showClear: false,
25319     
25320     /**
25321      * Sets the value of the date field
25322      * @param {Date} value The date to set
25323      */
25324     setValue : function(value){
25325         var old = this.value;
25326         this.value = value.clearTime(true);
25327         if(this.el){
25328             this.update(this.value);
25329         }
25330     },
25331
25332     /**
25333      * Gets the current selected value of the date field
25334      * @return {Date} The selected date
25335      */
25336     getValue : function(){
25337         return this.value;
25338     },
25339
25340     // private
25341     focus : function(){
25342         if(this.el){
25343             this.update(this.activeDate);
25344         }
25345     },
25346
25347     // private
25348     onRender : function(container, position){
25349         var m = [
25350              '<table cellspacing="0">',
25351                 '<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>',
25352                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
25353         var dn = this.dayNames;
25354         for(var i = 0; i < 7; i++){
25355             var d = this.startDay+i;
25356             if(d > 6){
25357                 d = d-7;
25358             }
25359             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
25360         }
25361         m[m.length] = "</tr></thead><tbody><tr>";
25362         for(var i = 0; i < 42; i++) {
25363             if(i % 7 == 0 && i != 0){
25364                 m[m.length] = "</tr><tr>";
25365             }
25366             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
25367         }
25368         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
25369             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
25370
25371         var el = document.createElement("div");
25372         el.className = "x-date-picker";
25373         el.innerHTML = m.join("");
25374
25375         container.dom.insertBefore(el, position);
25376
25377         this.el = Roo.get(el);
25378         this.eventEl = Roo.get(el.firstChild);
25379
25380         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
25381             handler: this.showPrevMonth,
25382             scope: this,
25383             preventDefault:true,
25384             stopDefault:true
25385         });
25386
25387         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
25388             handler: this.showNextMonth,
25389             scope: this,
25390             preventDefault:true,
25391             stopDefault:true
25392         });
25393
25394         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
25395
25396         this.monthPicker = this.el.down('div.x-date-mp');
25397         this.monthPicker.enableDisplayMode('block');
25398         
25399         var kn = new Roo.KeyNav(this.eventEl, {
25400             "left" : function(e){
25401                 e.ctrlKey ?
25402                     this.showPrevMonth() :
25403                     this.update(this.activeDate.add("d", -1));
25404             },
25405
25406             "right" : function(e){
25407                 e.ctrlKey ?
25408                     this.showNextMonth() :
25409                     this.update(this.activeDate.add("d", 1));
25410             },
25411
25412             "up" : function(e){
25413                 e.ctrlKey ?
25414                     this.showNextYear() :
25415                     this.update(this.activeDate.add("d", -7));
25416             },
25417
25418             "down" : function(e){
25419                 e.ctrlKey ?
25420                     this.showPrevYear() :
25421                     this.update(this.activeDate.add("d", 7));
25422             },
25423
25424             "pageUp" : function(e){
25425                 this.showNextMonth();
25426             },
25427
25428             "pageDown" : function(e){
25429                 this.showPrevMonth();
25430             },
25431
25432             "enter" : function(e){
25433                 e.stopPropagation();
25434                 return true;
25435             },
25436
25437             scope : this
25438         });
25439
25440         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
25441
25442         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
25443
25444         this.el.unselectable();
25445         
25446         this.cells = this.el.select("table.x-date-inner tbody td");
25447         this.textNodes = this.el.query("table.x-date-inner tbody span");
25448
25449         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
25450             text: "&#160;",
25451             tooltip: this.monthYearText
25452         });
25453
25454         this.mbtn.on('click', this.showMonthPicker, this);
25455         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
25456
25457
25458         var today = (new Date()).dateFormat(this.format);
25459         
25460         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
25461         if (this.showClear) {
25462             baseTb.add( new Roo.Toolbar.Fill());
25463         }
25464         baseTb.add({
25465             text: String.format(this.todayText, today),
25466             tooltip: String.format(this.todayTip, today),
25467             handler: this.selectToday,
25468             scope: this
25469         });
25470         
25471         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
25472             
25473         //});
25474         if (this.showClear) {
25475             
25476             baseTb.add( new Roo.Toolbar.Fill());
25477             baseTb.add({
25478                 text: '&#160;',
25479                 cls: 'x-btn-icon x-btn-clear',
25480                 handler: function() {
25481                     //this.value = '';
25482                     this.fireEvent("select", this, '');
25483                 },
25484                 scope: this
25485             });
25486         }
25487         
25488         
25489         if(Roo.isIE){
25490             this.el.repaint();
25491         }
25492         this.update(this.value);
25493     },
25494
25495     createMonthPicker : function(){
25496         if(!this.monthPicker.dom.firstChild){
25497             var buf = ['<table border="0" cellspacing="0">'];
25498             for(var i = 0; i < 6; i++){
25499                 buf.push(
25500                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
25501                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
25502                     i == 0 ?
25503                     '<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>' :
25504                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
25505                 );
25506             }
25507             buf.push(
25508                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
25509                     this.okText,
25510                     '</button><button type="button" class="x-date-mp-cancel">',
25511                     this.cancelText,
25512                     '</button></td></tr>',
25513                 '</table>'
25514             );
25515             this.monthPicker.update(buf.join(''));
25516             this.monthPicker.on('click', this.onMonthClick, this);
25517             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
25518
25519             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
25520             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
25521
25522             this.mpMonths.each(function(m, a, i){
25523                 i += 1;
25524                 if((i%2) == 0){
25525                     m.dom.xmonth = 5 + Math.round(i * .5);
25526                 }else{
25527                     m.dom.xmonth = Math.round((i-1) * .5);
25528                 }
25529             });
25530         }
25531     },
25532
25533     showMonthPicker : function(){
25534         this.createMonthPicker();
25535         var size = this.el.getSize();
25536         this.monthPicker.setSize(size);
25537         this.monthPicker.child('table').setSize(size);
25538
25539         this.mpSelMonth = (this.activeDate || this.value).getMonth();
25540         this.updateMPMonth(this.mpSelMonth);
25541         this.mpSelYear = (this.activeDate || this.value).getFullYear();
25542         this.updateMPYear(this.mpSelYear);
25543
25544         this.monthPicker.slideIn('t', {duration:.2});
25545     },
25546
25547     updateMPYear : function(y){
25548         this.mpyear = y;
25549         var ys = this.mpYears.elements;
25550         for(var i = 1; i <= 10; i++){
25551             var td = ys[i-1], y2;
25552             if((i%2) == 0){
25553                 y2 = y + Math.round(i * .5);
25554                 td.firstChild.innerHTML = y2;
25555                 td.xyear = y2;
25556             }else{
25557                 y2 = y - (5-Math.round(i * .5));
25558                 td.firstChild.innerHTML = y2;
25559                 td.xyear = y2;
25560             }
25561             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25562         }
25563     },
25564
25565     updateMPMonth : function(sm){
25566         this.mpMonths.each(function(m, a, i){
25567             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25568         });
25569     },
25570
25571     selectMPMonth: function(m){
25572         
25573     },
25574
25575     onMonthClick : function(e, t){
25576         e.stopEvent();
25577         var el = new Roo.Element(t), pn;
25578         if(el.is('button.x-date-mp-cancel')){
25579             this.hideMonthPicker();
25580         }
25581         else if(el.is('button.x-date-mp-ok')){
25582             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25583             this.hideMonthPicker();
25584         }
25585         else if(pn = el.up('td.x-date-mp-month', 2)){
25586             this.mpMonths.removeClass('x-date-mp-sel');
25587             pn.addClass('x-date-mp-sel');
25588             this.mpSelMonth = pn.dom.xmonth;
25589         }
25590         else if(pn = el.up('td.x-date-mp-year', 2)){
25591             this.mpYears.removeClass('x-date-mp-sel');
25592             pn.addClass('x-date-mp-sel');
25593             this.mpSelYear = pn.dom.xyear;
25594         }
25595         else if(el.is('a.x-date-mp-prev')){
25596             this.updateMPYear(this.mpyear-10);
25597         }
25598         else if(el.is('a.x-date-mp-next')){
25599             this.updateMPYear(this.mpyear+10);
25600         }
25601     },
25602
25603     onMonthDblClick : function(e, t){
25604         e.stopEvent();
25605         var el = new Roo.Element(t), pn;
25606         if(pn = el.up('td.x-date-mp-month', 2)){
25607             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25608             this.hideMonthPicker();
25609         }
25610         else if(pn = el.up('td.x-date-mp-year', 2)){
25611             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25612             this.hideMonthPicker();
25613         }
25614     },
25615
25616     hideMonthPicker : function(disableAnim){
25617         if(this.monthPicker){
25618             if(disableAnim === true){
25619                 this.monthPicker.hide();
25620             }else{
25621                 this.monthPicker.slideOut('t', {duration:.2});
25622             }
25623         }
25624     },
25625
25626     // private
25627     showPrevMonth : function(e){
25628         this.update(this.activeDate.add("mo", -1));
25629     },
25630
25631     // private
25632     showNextMonth : function(e){
25633         this.update(this.activeDate.add("mo", 1));
25634     },
25635
25636     // private
25637     showPrevYear : function(){
25638         this.update(this.activeDate.add("y", -1));
25639     },
25640
25641     // private
25642     showNextYear : function(){
25643         this.update(this.activeDate.add("y", 1));
25644     },
25645
25646     // private
25647     handleMouseWheel : function(e){
25648         var delta = e.getWheelDelta();
25649         if(delta > 0){
25650             this.showPrevMonth();
25651             e.stopEvent();
25652         } else if(delta < 0){
25653             this.showNextMonth();
25654             e.stopEvent();
25655         }
25656     },
25657
25658     // private
25659     handleDateClick : function(e, t){
25660         e.stopEvent();
25661         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25662             this.setValue(new Date(t.dateValue));
25663             this.fireEvent("select", this, this.value);
25664         }
25665     },
25666
25667     // private
25668     selectToday : function(){
25669         this.setValue(new Date().clearTime());
25670         this.fireEvent("select", this, this.value);
25671     },
25672
25673     // private
25674     update : function(date)
25675     {
25676         var vd = this.activeDate;
25677         this.activeDate = date;
25678         if(vd && this.el){
25679             var t = date.getTime();
25680             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25681                 this.cells.removeClass("x-date-selected");
25682                 this.cells.each(function(c){
25683                    if(c.dom.firstChild.dateValue == t){
25684                        c.addClass("x-date-selected");
25685                        setTimeout(function(){
25686                             try{c.dom.firstChild.focus();}catch(e){}
25687                        }, 50);
25688                        return false;
25689                    }
25690                 });
25691                 return;
25692             }
25693         }
25694         
25695         var days = date.getDaysInMonth();
25696         var firstOfMonth = date.getFirstDateOfMonth();
25697         var startingPos = firstOfMonth.getDay()-this.startDay;
25698
25699         if(startingPos <= this.startDay){
25700             startingPos += 7;
25701         }
25702
25703         var pm = date.add("mo", -1);
25704         var prevStart = pm.getDaysInMonth()-startingPos;
25705
25706         var cells = this.cells.elements;
25707         var textEls = this.textNodes;
25708         days += startingPos;
25709
25710         // convert everything to numbers so it's fast
25711         var day = 86400000;
25712         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25713         var today = new Date().clearTime().getTime();
25714         var sel = date.clearTime().getTime();
25715         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25716         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25717         var ddMatch = this.disabledDatesRE;
25718         var ddText = this.disabledDatesText;
25719         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25720         var ddaysText = this.disabledDaysText;
25721         var format = this.format;
25722
25723         var setCellClass = function(cal, cell){
25724             cell.title = "";
25725             var t = d.getTime();
25726             cell.firstChild.dateValue = t;
25727             if(t == today){
25728                 cell.className += " x-date-today";
25729                 cell.title = cal.todayText;
25730             }
25731             if(t == sel){
25732                 cell.className += " x-date-selected";
25733                 setTimeout(function(){
25734                     try{cell.firstChild.focus();}catch(e){}
25735                 }, 50);
25736             }
25737             // disabling
25738             if(t < min) {
25739                 cell.className = " x-date-disabled";
25740                 cell.title = cal.minText;
25741                 return;
25742             }
25743             if(t > max) {
25744                 cell.className = " x-date-disabled";
25745                 cell.title = cal.maxText;
25746                 return;
25747             }
25748             if(ddays){
25749                 if(ddays.indexOf(d.getDay()) != -1){
25750                     cell.title = ddaysText;
25751                     cell.className = " x-date-disabled";
25752                 }
25753             }
25754             if(ddMatch && format){
25755                 var fvalue = d.dateFormat(format);
25756                 if(ddMatch.test(fvalue)){
25757                     cell.title = ddText.replace("%0", fvalue);
25758                     cell.className = " x-date-disabled";
25759                 }
25760             }
25761         };
25762
25763         var i = 0;
25764         for(; i < startingPos; i++) {
25765             textEls[i].innerHTML = (++prevStart);
25766             d.setDate(d.getDate()+1);
25767             cells[i].className = "x-date-prevday";
25768             setCellClass(this, cells[i]);
25769         }
25770         for(; i < days; i++){
25771             intDay = i - startingPos + 1;
25772             textEls[i].innerHTML = (intDay);
25773             d.setDate(d.getDate()+1);
25774             cells[i].className = "x-date-active";
25775             setCellClass(this, cells[i]);
25776         }
25777         var extraDays = 0;
25778         for(; i < 42; i++) {
25779              textEls[i].innerHTML = (++extraDays);
25780              d.setDate(d.getDate()+1);
25781              cells[i].className = "x-date-nextday";
25782              setCellClass(this, cells[i]);
25783         }
25784
25785         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25786         this.fireEvent('monthchange', this, date);
25787         
25788         if(!this.internalRender){
25789             var main = this.el.dom.firstChild;
25790             var w = main.offsetWidth;
25791             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25792             Roo.fly(main).setWidth(w);
25793             this.internalRender = true;
25794             // opera does not respect the auto grow header center column
25795             // then, after it gets a width opera refuses to recalculate
25796             // without a second pass
25797             if(Roo.isOpera && !this.secondPass){
25798                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25799                 this.secondPass = true;
25800                 this.update.defer(10, this, [date]);
25801             }
25802         }
25803         
25804         
25805     }
25806 });        /*
25807  * Based on:
25808  * Ext JS Library 1.1.1
25809  * Copyright(c) 2006-2007, Ext JS, LLC.
25810  *
25811  * Originally Released Under LGPL - original licence link has changed is not relivant.
25812  *
25813  * Fork - LGPL
25814  * <script type="text/javascript">
25815  */
25816 /**
25817  * @class Roo.TabPanel
25818  * @extends Roo.util.Observable
25819  * A lightweight tab container.
25820  * <br><br>
25821  * Usage:
25822  * <pre><code>
25823 // basic tabs 1, built from existing content
25824 var tabs = new Roo.TabPanel("tabs1");
25825 tabs.addTab("script", "View Script");
25826 tabs.addTab("markup", "View Markup");
25827 tabs.activate("script");
25828
25829 // more advanced tabs, built from javascript
25830 var jtabs = new Roo.TabPanel("jtabs");
25831 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25832
25833 // set up the UpdateManager
25834 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25835 var updater = tab2.getUpdateManager();
25836 updater.setDefaultUrl("ajax1.htm");
25837 tab2.on('activate', updater.refresh, updater, true);
25838
25839 // Use setUrl for Ajax loading
25840 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25841 tab3.setUrl("ajax2.htm", null, true);
25842
25843 // Disabled tab
25844 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25845 tab4.disable();
25846
25847 jtabs.activate("jtabs-1");
25848  * </code></pre>
25849  * @constructor
25850  * Create a new TabPanel.
25851  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25852  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25853  */
25854 Roo.TabPanel = function(container, config){
25855     /**
25856     * The container element for this TabPanel.
25857     * @type Roo.Element
25858     */
25859     this.el = Roo.get(container, true);
25860     if(config){
25861         if(typeof config == "boolean"){
25862             this.tabPosition = config ? "bottom" : "top";
25863         }else{
25864             Roo.apply(this, config);
25865         }
25866     }
25867     if(this.tabPosition == "bottom"){
25868         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25869         this.el.addClass("x-tabs-bottom");
25870     }
25871     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25872     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25873     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25874     if(Roo.isIE){
25875         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25876     }
25877     if(this.tabPosition != "bottom"){
25878         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25879          * @type Roo.Element
25880          */
25881         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25882         this.el.addClass("x-tabs-top");
25883     }
25884     this.items = [];
25885
25886     this.bodyEl.setStyle("position", "relative");
25887
25888     this.active = null;
25889     this.activateDelegate = this.activate.createDelegate(this);
25890
25891     this.addEvents({
25892         /**
25893          * @event tabchange
25894          * Fires when the active tab changes
25895          * @param {Roo.TabPanel} this
25896          * @param {Roo.TabPanelItem} activePanel The new active tab
25897          */
25898         "tabchange": true,
25899         /**
25900          * @event beforetabchange
25901          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25902          * @param {Roo.TabPanel} this
25903          * @param {Object} e Set cancel to true on this object to cancel the tab change
25904          * @param {Roo.TabPanelItem} tab The tab being changed to
25905          */
25906         "beforetabchange" : true
25907     });
25908
25909     Roo.EventManager.onWindowResize(this.onResize, this);
25910     this.cpad = this.el.getPadding("lr");
25911     this.hiddenCount = 0;
25912
25913
25914     // toolbar on the tabbar support...
25915     if (this.toolbar) {
25916         var tcfg = this.toolbar;
25917         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25918         this.toolbar = new Roo.Toolbar(tcfg);
25919         if (Roo.isSafari) {
25920             var tbl = tcfg.container.child('table', true);
25921             tbl.setAttribute('width', '100%');
25922         }
25923         
25924     }
25925    
25926
25927
25928     Roo.TabPanel.superclass.constructor.call(this);
25929 };
25930
25931 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25932     /*
25933      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25934      */
25935     tabPosition : "top",
25936     /*
25937      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25938      */
25939     currentTabWidth : 0,
25940     /*
25941      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25942      */
25943     minTabWidth : 40,
25944     /*
25945      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25946      */
25947     maxTabWidth : 250,
25948     /*
25949      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25950      */
25951     preferredTabWidth : 175,
25952     /*
25953      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25954      */
25955     resizeTabs : false,
25956     /*
25957      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25958      */
25959     monitorResize : true,
25960     /*
25961      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25962      */
25963     toolbar : false,
25964
25965     /**
25966      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25967      * @param {String} id The id of the div to use <b>or create</b>
25968      * @param {String} text The text for the tab
25969      * @param {String} content (optional) Content to put in the TabPanelItem body
25970      * @param {Boolean} closable (optional) True to create a close icon on the tab
25971      * @return {Roo.TabPanelItem} The created TabPanelItem
25972      */
25973     addTab : function(id, text, content, closable){
25974         var item = new Roo.TabPanelItem(this, id, text, closable);
25975         this.addTabItem(item);
25976         if(content){
25977             item.setContent(content);
25978         }
25979         return item;
25980     },
25981
25982     /**
25983      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25984      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25985      * @return {Roo.TabPanelItem}
25986      */
25987     getTab : function(id){
25988         return this.items[id];
25989     },
25990
25991     /**
25992      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25993      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25994      */
25995     hideTab : function(id){
25996         var t = this.items[id];
25997         if(!t.isHidden()){
25998            t.setHidden(true);
25999            this.hiddenCount++;
26000            this.autoSizeTabs();
26001         }
26002     },
26003
26004     /**
26005      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26006      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26007      */
26008     unhideTab : function(id){
26009         var t = this.items[id];
26010         if(t.isHidden()){
26011            t.setHidden(false);
26012            this.hiddenCount--;
26013            this.autoSizeTabs();
26014         }
26015     },
26016
26017     /**
26018      * Adds an existing {@link Roo.TabPanelItem}.
26019      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26020      */
26021     addTabItem : function(item){
26022         this.items[item.id] = item;
26023         this.items.push(item);
26024         if(this.resizeTabs){
26025            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26026            this.autoSizeTabs();
26027         }else{
26028             item.autoSize();
26029         }
26030     },
26031
26032     /**
26033      * Removes a {@link Roo.TabPanelItem}.
26034      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26035      */
26036     removeTab : function(id){
26037         var items = this.items;
26038         var tab = items[id];
26039         if(!tab) { return; }
26040         var index = items.indexOf(tab);
26041         if(this.active == tab && items.length > 1){
26042             var newTab = this.getNextAvailable(index);
26043             if(newTab) {
26044                 newTab.activate();
26045             }
26046         }
26047         this.stripEl.dom.removeChild(tab.pnode.dom);
26048         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26049             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26050         }
26051         items.splice(index, 1);
26052         delete this.items[tab.id];
26053         tab.fireEvent("close", tab);
26054         tab.purgeListeners();
26055         this.autoSizeTabs();
26056     },
26057
26058     getNextAvailable : function(start){
26059         var items = this.items;
26060         var index = start;
26061         // look for a next tab that will slide over to
26062         // replace the one being removed
26063         while(index < items.length){
26064             var item = items[++index];
26065             if(item && !item.isHidden()){
26066                 return item;
26067             }
26068         }
26069         // if one isn't found select the previous tab (on the left)
26070         index = start;
26071         while(index >= 0){
26072             var item = items[--index];
26073             if(item && !item.isHidden()){
26074                 return item;
26075             }
26076         }
26077         return null;
26078     },
26079
26080     /**
26081      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26082      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26083      */
26084     disableTab : function(id){
26085         var tab = this.items[id];
26086         if(tab && this.active != tab){
26087             tab.disable();
26088         }
26089     },
26090
26091     /**
26092      * Enables a {@link Roo.TabPanelItem} that is disabled.
26093      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26094      */
26095     enableTab : function(id){
26096         var tab = this.items[id];
26097         tab.enable();
26098     },
26099
26100     /**
26101      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26102      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26103      * @return {Roo.TabPanelItem} The TabPanelItem.
26104      */
26105     activate : function(id){
26106         var tab = this.items[id];
26107         if(!tab){
26108             return null;
26109         }
26110         if(tab == this.active || tab.disabled){
26111             return tab;
26112         }
26113         var e = {};
26114         this.fireEvent("beforetabchange", this, e, tab);
26115         if(e.cancel !== true && !tab.disabled){
26116             if(this.active){
26117                 this.active.hide();
26118             }
26119             this.active = this.items[id];
26120             this.active.show();
26121             this.fireEvent("tabchange", this, this.active);
26122         }
26123         return tab;
26124     },
26125
26126     /**
26127      * Gets the active {@link Roo.TabPanelItem}.
26128      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26129      */
26130     getActiveTab : function(){
26131         return this.active;
26132     },
26133
26134     /**
26135      * Updates the tab body element to fit the height of the container element
26136      * for overflow scrolling
26137      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26138      */
26139     syncHeight : function(targetHeight){
26140         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26141         var bm = this.bodyEl.getMargins();
26142         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26143         this.bodyEl.setHeight(newHeight);
26144         return newHeight;
26145     },
26146
26147     onResize : function(){
26148         if(this.monitorResize){
26149             this.autoSizeTabs();
26150         }
26151     },
26152
26153     /**
26154      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26155      */
26156     beginUpdate : function(){
26157         this.updating = true;
26158     },
26159
26160     /**
26161      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26162      */
26163     endUpdate : function(){
26164         this.updating = false;
26165         this.autoSizeTabs();
26166     },
26167
26168     /**
26169      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26170      */
26171     autoSizeTabs : function(){
26172         var count = this.items.length;
26173         var vcount = count - this.hiddenCount;
26174         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26175         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26176         var availWidth = Math.floor(w / vcount);
26177         var b = this.stripBody;
26178         if(b.getWidth() > w){
26179             var tabs = this.items;
26180             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26181             if(availWidth < this.minTabWidth){
26182                 /*if(!this.sleft){    // incomplete scrolling code
26183                     this.createScrollButtons();
26184                 }
26185                 this.showScroll();
26186                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26187             }
26188         }else{
26189             if(this.currentTabWidth < this.preferredTabWidth){
26190                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26191             }
26192         }
26193     },
26194
26195     /**
26196      * Returns the number of tabs in this TabPanel.
26197      * @return {Number}
26198      */
26199      getCount : function(){
26200          return this.items.length;
26201      },
26202
26203     /**
26204      * Resizes all the tabs to the passed width
26205      * @param {Number} The new width
26206      */
26207     setTabWidth : function(width){
26208         this.currentTabWidth = width;
26209         for(var i = 0, len = this.items.length; i < len; i++) {
26210                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26211         }
26212     },
26213
26214     /**
26215      * Destroys this TabPanel
26216      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26217      */
26218     destroy : function(removeEl){
26219         Roo.EventManager.removeResizeListener(this.onResize, this);
26220         for(var i = 0, len = this.items.length; i < len; i++){
26221             this.items[i].purgeListeners();
26222         }
26223         if(removeEl === true){
26224             this.el.update("");
26225             this.el.remove();
26226         }
26227     }
26228 });
26229
26230 /**
26231  * @class Roo.TabPanelItem
26232  * @extends Roo.util.Observable
26233  * Represents an individual item (tab plus body) in a TabPanel.
26234  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26235  * @param {String} id The id of this TabPanelItem
26236  * @param {String} text The text for the tab of this TabPanelItem
26237  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26238  */
26239 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26240     /**
26241      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26242      * @type Roo.TabPanel
26243      */
26244     this.tabPanel = tabPanel;
26245     /**
26246      * The id for this TabPanelItem
26247      * @type String
26248      */
26249     this.id = id;
26250     /** @private */
26251     this.disabled = false;
26252     /** @private */
26253     this.text = text;
26254     /** @private */
26255     this.loaded = false;
26256     this.closable = closable;
26257
26258     /**
26259      * The body element for this TabPanelItem.
26260      * @type Roo.Element
26261      */
26262     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26263     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26264     this.bodyEl.setStyle("display", "block");
26265     this.bodyEl.setStyle("zoom", "1");
26266     this.hideAction();
26267
26268     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26269     /** @private */
26270     this.el = Roo.get(els.el, true);
26271     this.inner = Roo.get(els.inner, true);
26272     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26273     this.pnode = Roo.get(els.el.parentNode, true);
26274     this.el.on("mousedown", this.onTabMouseDown, this);
26275     this.el.on("click", this.onTabClick, this);
26276     /** @private */
26277     if(closable){
26278         var c = Roo.get(els.close, true);
26279         c.dom.title = this.closeText;
26280         c.addClassOnOver("close-over");
26281         c.on("click", this.closeClick, this);
26282      }
26283
26284     this.addEvents({
26285          /**
26286          * @event activate
26287          * Fires when this tab becomes the active tab.
26288          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26289          * @param {Roo.TabPanelItem} this
26290          */
26291         "activate": true,
26292         /**
26293          * @event beforeclose
26294          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26295          * @param {Roo.TabPanelItem} this
26296          * @param {Object} e Set cancel to true on this object to cancel the close.
26297          */
26298         "beforeclose": true,
26299         /**
26300          * @event close
26301          * Fires when this tab is closed.
26302          * @param {Roo.TabPanelItem} this
26303          */
26304          "close": true,
26305         /**
26306          * @event deactivate
26307          * Fires when this tab is no longer the active tab.
26308          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26309          * @param {Roo.TabPanelItem} this
26310          */
26311          "deactivate" : true
26312     });
26313     this.hidden = false;
26314
26315     Roo.TabPanelItem.superclass.constructor.call(this);
26316 };
26317
26318 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26319     purgeListeners : function(){
26320        Roo.util.Observable.prototype.purgeListeners.call(this);
26321        this.el.removeAllListeners();
26322     },
26323     /**
26324      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26325      */
26326     show : function(){
26327         this.pnode.addClass("on");
26328         this.showAction();
26329         if(Roo.isOpera){
26330             this.tabPanel.stripWrap.repaint();
26331         }
26332         this.fireEvent("activate", this.tabPanel, this);
26333     },
26334
26335     /**
26336      * Returns true if this tab is the active tab.
26337      * @return {Boolean}
26338      */
26339     isActive : function(){
26340         return this.tabPanel.getActiveTab() == this;
26341     },
26342
26343     /**
26344      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
26345      */
26346     hide : function(){
26347         this.pnode.removeClass("on");
26348         this.hideAction();
26349         this.fireEvent("deactivate", this.tabPanel, this);
26350     },
26351
26352     hideAction : function(){
26353         this.bodyEl.hide();
26354         this.bodyEl.setStyle("position", "absolute");
26355         this.bodyEl.setLeft("-20000px");
26356         this.bodyEl.setTop("-20000px");
26357     },
26358
26359     showAction : function(){
26360         this.bodyEl.setStyle("position", "relative");
26361         this.bodyEl.setTop("");
26362         this.bodyEl.setLeft("");
26363         this.bodyEl.show();
26364     },
26365
26366     /**
26367      * Set the tooltip for the tab.
26368      * @param {String} tooltip The tab's tooltip
26369      */
26370     setTooltip : function(text){
26371         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
26372             this.textEl.dom.qtip = text;
26373             this.textEl.dom.removeAttribute('title');
26374         }else{
26375             this.textEl.dom.title = text;
26376         }
26377     },
26378
26379     onTabClick : function(e){
26380         e.preventDefault();
26381         this.tabPanel.activate(this.id);
26382     },
26383
26384     onTabMouseDown : function(e){
26385         e.preventDefault();
26386         this.tabPanel.activate(this.id);
26387     },
26388
26389     getWidth : function(){
26390         return this.inner.getWidth();
26391     },
26392
26393     setWidth : function(width){
26394         var iwidth = width - this.pnode.getPadding("lr");
26395         this.inner.setWidth(iwidth);
26396         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
26397         this.pnode.setWidth(width);
26398     },
26399
26400     /**
26401      * Show or hide the tab
26402      * @param {Boolean} hidden True to hide or false to show.
26403      */
26404     setHidden : function(hidden){
26405         this.hidden = hidden;
26406         this.pnode.setStyle("display", hidden ? "none" : "");
26407     },
26408
26409     /**
26410      * Returns true if this tab is "hidden"
26411      * @return {Boolean}
26412      */
26413     isHidden : function(){
26414         return this.hidden;
26415     },
26416
26417     /**
26418      * Returns the text for this tab
26419      * @return {String}
26420      */
26421     getText : function(){
26422         return this.text;
26423     },
26424
26425     autoSize : function(){
26426         //this.el.beginMeasure();
26427         this.textEl.setWidth(1);
26428         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
26429         //this.el.endMeasure();
26430     },
26431
26432     /**
26433      * Sets the text for the tab (Note: this also sets the tooltip text)
26434      * @param {String} text The tab's text and tooltip
26435      */
26436     setText : function(text){
26437         this.text = text;
26438         this.textEl.update(text);
26439         this.setTooltip(text);
26440         if(!this.tabPanel.resizeTabs){
26441             this.autoSize();
26442         }
26443     },
26444     /**
26445      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
26446      */
26447     activate : function(){
26448         this.tabPanel.activate(this.id);
26449     },
26450
26451     /**
26452      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
26453      */
26454     disable : function(){
26455         if(this.tabPanel.active != this){
26456             this.disabled = true;
26457             this.pnode.addClass("disabled");
26458         }
26459     },
26460
26461     /**
26462      * Enables this TabPanelItem if it was previously disabled.
26463      */
26464     enable : function(){
26465         this.disabled = false;
26466         this.pnode.removeClass("disabled");
26467     },
26468
26469     /**
26470      * Sets the content for this TabPanelItem.
26471      * @param {String} content The content
26472      * @param {Boolean} loadScripts true to look for and load scripts
26473      */
26474     setContent : function(content, loadScripts){
26475         this.bodyEl.update(content, loadScripts);
26476     },
26477
26478     /**
26479      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
26480      * @return {Roo.UpdateManager} The UpdateManager
26481      */
26482     getUpdateManager : function(){
26483         return this.bodyEl.getUpdateManager();
26484     },
26485
26486     /**
26487      * Set a URL to be used to load the content for this TabPanelItem.
26488      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
26489      * @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)
26490      * @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)
26491      * @return {Roo.UpdateManager} The UpdateManager
26492      */
26493     setUrl : function(url, params, loadOnce){
26494         if(this.refreshDelegate){
26495             this.un('activate', this.refreshDelegate);
26496         }
26497         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
26498         this.on("activate", this.refreshDelegate);
26499         return this.bodyEl.getUpdateManager();
26500     },
26501
26502     /** @private */
26503     _handleRefresh : function(url, params, loadOnce){
26504         if(!loadOnce || !this.loaded){
26505             var updater = this.bodyEl.getUpdateManager();
26506             updater.update(url, params, this._setLoaded.createDelegate(this));
26507         }
26508     },
26509
26510     /**
26511      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
26512      *   Will fail silently if the setUrl method has not been called.
26513      *   This does not activate the panel, just updates its content.
26514      */
26515     refresh : function(){
26516         if(this.refreshDelegate){
26517            this.loaded = false;
26518            this.refreshDelegate();
26519         }
26520     },
26521
26522     /** @private */
26523     _setLoaded : function(){
26524         this.loaded = true;
26525     },
26526
26527     /** @private */
26528     closeClick : function(e){
26529         var o = {};
26530         e.stopEvent();
26531         this.fireEvent("beforeclose", this, o);
26532         if(o.cancel !== true){
26533             this.tabPanel.removeTab(this.id);
26534         }
26535     },
26536     /**
26537      * The text displayed in the tooltip for the close icon.
26538      * @type String
26539      */
26540     closeText : "Close this tab"
26541 });
26542
26543 /** @private */
26544 Roo.TabPanel.prototype.createStrip = function(container){
26545     var strip = document.createElement("div");
26546     strip.className = "x-tabs-wrap";
26547     container.appendChild(strip);
26548     return strip;
26549 };
26550 /** @private */
26551 Roo.TabPanel.prototype.createStripList = function(strip){
26552     // div wrapper for retard IE
26553     // returns the "tr" element.
26554     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26555         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26556         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26557     return strip.firstChild.firstChild.firstChild.firstChild;
26558 };
26559 /** @private */
26560 Roo.TabPanel.prototype.createBody = function(container){
26561     var body = document.createElement("div");
26562     Roo.id(body, "tab-body");
26563     Roo.fly(body).addClass("x-tabs-body");
26564     container.appendChild(body);
26565     return body;
26566 };
26567 /** @private */
26568 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26569     var body = Roo.getDom(id);
26570     if(!body){
26571         body = document.createElement("div");
26572         body.id = id;
26573     }
26574     Roo.fly(body).addClass("x-tabs-item-body");
26575     bodyEl.insertBefore(body, bodyEl.firstChild);
26576     return body;
26577 };
26578 /** @private */
26579 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26580     var td = document.createElement("td");
26581     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26582     //stripEl.appendChild(td);
26583     if(closable){
26584         td.className = "x-tabs-closable";
26585         if(!this.closeTpl){
26586             this.closeTpl = new Roo.Template(
26587                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26588                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26589                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26590             );
26591         }
26592         var el = this.closeTpl.overwrite(td, {"text": text});
26593         var close = el.getElementsByTagName("div")[0];
26594         var inner = el.getElementsByTagName("em")[0];
26595         return {"el": el, "close": close, "inner": inner};
26596     } else {
26597         if(!this.tabTpl){
26598             this.tabTpl = new Roo.Template(
26599                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26600                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26601             );
26602         }
26603         var el = this.tabTpl.overwrite(td, {"text": text});
26604         var inner = el.getElementsByTagName("em")[0];
26605         return {"el": el, "inner": inner};
26606     }
26607 };/*
26608  * Based on:
26609  * Ext JS Library 1.1.1
26610  * Copyright(c) 2006-2007, Ext JS, LLC.
26611  *
26612  * Originally Released Under LGPL - original licence link has changed is not relivant.
26613  *
26614  * Fork - LGPL
26615  * <script type="text/javascript">
26616  */
26617
26618 /**
26619  * @class Roo.Button
26620  * @extends Roo.util.Observable
26621  * Simple Button class
26622  * @cfg {String} text The button text
26623  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26624  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26625  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26626  * @cfg {Object} scope The scope of the handler
26627  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26628  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26629  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26630  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26631  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26632  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26633    applies if enableToggle = true)
26634  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26635  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26636   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26637  * @constructor
26638  * Create a new button
26639  * @param {Object} config The config object
26640  */
26641 Roo.Button = function(renderTo, config)
26642 {
26643     if (!config) {
26644         config = renderTo;
26645         renderTo = config.renderTo || false;
26646     }
26647     
26648     Roo.apply(this, config);
26649     this.addEvents({
26650         /**
26651              * @event click
26652              * Fires when this button is clicked
26653              * @param {Button} this
26654              * @param {EventObject} e The click event
26655              */
26656             "click" : true,
26657         /**
26658              * @event toggle
26659              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26660              * @param {Button} this
26661              * @param {Boolean} pressed
26662              */
26663             "toggle" : true,
26664         /**
26665              * @event mouseover
26666              * Fires when the mouse hovers over the button
26667              * @param {Button} this
26668              * @param {Event} e The event object
26669              */
26670         'mouseover' : true,
26671         /**
26672              * @event mouseout
26673              * Fires when the mouse exits the button
26674              * @param {Button} this
26675              * @param {Event} e The event object
26676              */
26677         'mouseout': true,
26678          /**
26679              * @event render
26680              * Fires when the button is rendered
26681              * @param {Button} this
26682              */
26683         'render': true
26684     });
26685     if(this.menu){
26686         this.menu = Roo.menu.MenuMgr.get(this.menu);
26687     }
26688     // register listeners first!!  - so render can be captured..
26689     Roo.util.Observable.call(this);
26690     if(renderTo){
26691         this.render(renderTo);
26692     }
26693     
26694   
26695 };
26696
26697 Roo.extend(Roo.Button, Roo.util.Observable, {
26698     /**
26699      * 
26700      */
26701     
26702     /**
26703      * Read-only. True if this button is hidden
26704      * @type Boolean
26705      */
26706     hidden : false,
26707     /**
26708      * Read-only. True if this button is disabled
26709      * @type Boolean
26710      */
26711     disabled : false,
26712     /**
26713      * Read-only. True if this button is pressed (only if enableToggle = true)
26714      * @type Boolean
26715      */
26716     pressed : false,
26717
26718     /**
26719      * @cfg {Number} tabIndex 
26720      * The DOM tabIndex for this button (defaults to undefined)
26721      */
26722     tabIndex : undefined,
26723
26724     /**
26725      * @cfg {Boolean} enableToggle
26726      * True to enable pressed/not pressed toggling (defaults to false)
26727      */
26728     enableToggle: false,
26729     /**
26730      * @cfg {Mixed} menu
26731      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26732      */
26733     menu : undefined,
26734     /**
26735      * @cfg {String} menuAlign
26736      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26737      */
26738     menuAlign : "tl-bl?",
26739
26740     /**
26741      * @cfg {String} iconCls
26742      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26743      */
26744     iconCls : undefined,
26745     /**
26746      * @cfg {String} type
26747      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26748      */
26749     type : 'button',
26750
26751     // private
26752     menuClassTarget: 'tr',
26753
26754     /**
26755      * @cfg {String} clickEvent
26756      * The type of event to map to the button's event handler (defaults to 'click')
26757      */
26758     clickEvent : 'click',
26759
26760     /**
26761      * @cfg {Boolean} handleMouseEvents
26762      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26763      */
26764     handleMouseEvents : true,
26765
26766     /**
26767      * @cfg {String} tooltipType
26768      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26769      */
26770     tooltipType : 'qtip',
26771
26772     /**
26773      * @cfg {String} cls
26774      * A CSS class to apply to the button's main element.
26775      */
26776     
26777     /**
26778      * @cfg {Roo.Template} template (Optional)
26779      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26780      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26781      * require code modifications if required elements (e.g. a button) aren't present.
26782      */
26783
26784     // private
26785     render : function(renderTo){
26786         var btn;
26787         if(this.hideParent){
26788             this.parentEl = Roo.get(renderTo);
26789         }
26790         if(!this.dhconfig){
26791             if(!this.template){
26792                 if(!Roo.Button.buttonTemplate){
26793                     // hideous table template
26794                     Roo.Button.buttonTemplate = new Roo.Template(
26795                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26796                         '<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>',
26797                         "</tr></tbody></table>");
26798                 }
26799                 this.template = Roo.Button.buttonTemplate;
26800             }
26801             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26802             var btnEl = btn.child("button:first");
26803             btnEl.on('focus', this.onFocus, this);
26804             btnEl.on('blur', this.onBlur, this);
26805             if(this.cls){
26806                 btn.addClass(this.cls);
26807             }
26808             if(this.icon){
26809                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26810             }
26811             if(this.iconCls){
26812                 btnEl.addClass(this.iconCls);
26813                 if(!this.cls){
26814                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26815                 }
26816             }
26817             if(this.tabIndex !== undefined){
26818                 btnEl.dom.tabIndex = this.tabIndex;
26819             }
26820             if(this.tooltip){
26821                 if(typeof this.tooltip == 'object'){
26822                     Roo.QuickTips.tips(Roo.apply({
26823                           target: btnEl.id
26824                     }, this.tooltip));
26825                 } else {
26826                     btnEl.dom[this.tooltipType] = this.tooltip;
26827                 }
26828             }
26829         }else{
26830             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26831         }
26832         this.el = btn;
26833         if(this.id){
26834             this.el.dom.id = this.el.id = this.id;
26835         }
26836         if(this.menu){
26837             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26838             this.menu.on("show", this.onMenuShow, this);
26839             this.menu.on("hide", this.onMenuHide, this);
26840         }
26841         btn.addClass("x-btn");
26842         if(Roo.isIE && !Roo.isIE7){
26843             this.autoWidth.defer(1, this);
26844         }else{
26845             this.autoWidth();
26846         }
26847         if(this.handleMouseEvents){
26848             btn.on("mouseover", this.onMouseOver, this);
26849             btn.on("mouseout", this.onMouseOut, this);
26850             btn.on("mousedown", this.onMouseDown, this);
26851         }
26852         btn.on(this.clickEvent, this.onClick, this);
26853         //btn.on("mouseup", this.onMouseUp, this);
26854         if(this.hidden){
26855             this.hide();
26856         }
26857         if(this.disabled){
26858             this.disable();
26859         }
26860         Roo.ButtonToggleMgr.register(this);
26861         if(this.pressed){
26862             this.el.addClass("x-btn-pressed");
26863         }
26864         if(this.repeat){
26865             var repeater = new Roo.util.ClickRepeater(btn,
26866                 typeof this.repeat == "object" ? this.repeat : {}
26867             );
26868             repeater.on("click", this.onClick,  this);
26869         }
26870         
26871         this.fireEvent('render', this);
26872         
26873     },
26874     /**
26875      * Returns the button's underlying element
26876      * @return {Roo.Element} The element
26877      */
26878     getEl : function(){
26879         return this.el;  
26880     },
26881     
26882     /**
26883      * Destroys this Button and removes any listeners.
26884      */
26885     destroy : function(){
26886         Roo.ButtonToggleMgr.unregister(this);
26887         this.el.removeAllListeners();
26888         this.purgeListeners();
26889         this.el.remove();
26890     },
26891
26892     // private
26893     autoWidth : function(){
26894         if(this.el){
26895             this.el.setWidth("auto");
26896             if(Roo.isIE7 && Roo.isStrict){
26897                 var ib = this.el.child('button');
26898                 if(ib && ib.getWidth() > 20){
26899                     ib.clip();
26900                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26901                 }
26902             }
26903             if(this.minWidth){
26904                 if(this.hidden){
26905                     this.el.beginMeasure();
26906                 }
26907                 if(this.el.getWidth() < this.minWidth){
26908                     this.el.setWidth(this.minWidth);
26909                 }
26910                 if(this.hidden){
26911                     this.el.endMeasure();
26912                 }
26913             }
26914         }
26915     },
26916
26917     /**
26918      * Assigns this button's click handler
26919      * @param {Function} handler The function to call when the button is clicked
26920      * @param {Object} scope (optional) Scope for the function passed in
26921      */
26922     setHandler : function(handler, scope){
26923         this.handler = handler;
26924         this.scope = scope;  
26925     },
26926     
26927     /**
26928      * Sets this button's text
26929      * @param {String} text The button text
26930      */
26931     setText : function(text){
26932         this.text = text;
26933         if(this.el){
26934             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26935         }
26936         this.autoWidth();
26937     },
26938     
26939     /**
26940      * Gets the text for this button
26941      * @return {String} The button text
26942      */
26943     getText : function(){
26944         return this.text;  
26945     },
26946     
26947     /**
26948      * Show this button
26949      */
26950     show: function(){
26951         this.hidden = false;
26952         if(this.el){
26953             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26954         }
26955     },
26956     
26957     /**
26958      * Hide this button
26959      */
26960     hide: function(){
26961         this.hidden = true;
26962         if(this.el){
26963             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26964         }
26965     },
26966     
26967     /**
26968      * Convenience function for boolean show/hide
26969      * @param {Boolean} visible True to show, false to hide
26970      */
26971     setVisible: function(visible){
26972         if(visible) {
26973             this.show();
26974         }else{
26975             this.hide();
26976         }
26977     },
26978     
26979     /**
26980      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26981      * @param {Boolean} state (optional) Force a particular state
26982      */
26983     toggle : function(state){
26984         state = state === undefined ? !this.pressed : state;
26985         if(state != this.pressed){
26986             if(state){
26987                 this.el.addClass("x-btn-pressed");
26988                 this.pressed = true;
26989                 this.fireEvent("toggle", this, true);
26990             }else{
26991                 this.el.removeClass("x-btn-pressed");
26992                 this.pressed = false;
26993                 this.fireEvent("toggle", this, false);
26994             }
26995             if(this.toggleHandler){
26996                 this.toggleHandler.call(this.scope || this, this, state);
26997             }
26998         }
26999     },
27000     
27001     /**
27002      * Focus the button
27003      */
27004     focus : function(){
27005         this.el.child('button:first').focus();
27006     },
27007     
27008     /**
27009      * Disable this button
27010      */
27011     disable : function(){
27012         if(this.el){
27013             this.el.addClass("x-btn-disabled");
27014         }
27015         this.disabled = true;
27016     },
27017     
27018     /**
27019      * Enable this button
27020      */
27021     enable : function(){
27022         if(this.el){
27023             this.el.removeClass("x-btn-disabled");
27024         }
27025         this.disabled = false;
27026     },
27027
27028     /**
27029      * Convenience function for boolean enable/disable
27030      * @param {Boolean} enabled True to enable, false to disable
27031      */
27032     setDisabled : function(v){
27033         this[v !== true ? "enable" : "disable"]();
27034     },
27035
27036     // private
27037     onClick : function(e){
27038         if(e){
27039             e.preventDefault();
27040         }
27041         if(e.button != 0){
27042             return;
27043         }
27044         if(!this.disabled){
27045             if(this.enableToggle){
27046                 this.toggle();
27047             }
27048             if(this.menu && !this.menu.isVisible()){
27049                 this.menu.show(this.el, this.menuAlign);
27050             }
27051             this.fireEvent("click", this, e);
27052             if(this.handler){
27053                 this.el.removeClass("x-btn-over");
27054                 this.handler.call(this.scope || this, this, e);
27055             }
27056         }
27057     },
27058     // private
27059     onMouseOver : function(e){
27060         if(!this.disabled){
27061             this.el.addClass("x-btn-over");
27062             this.fireEvent('mouseover', this, e);
27063         }
27064     },
27065     // private
27066     onMouseOut : function(e){
27067         if(!e.within(this.el,  true)){
27068             this.el.removeClass("x-btn-over");
27069             this.fireEvent('mouseout', this, e);
27070         }
27071     },
27072     // private
27073     onFocus : function(e){
27074         if(!this.disabled){
27075             this.el.addClass("x-btn-focus");
27076         }
27077     },
27078     // private
27079     onBlur : function(e){
27080         this.el.removeClass("x-btn-focus");
27081     },
27082     // private
27083     onMouseDown : function(e){
27084         if(!this.disabled && e.button == 0){
27085             this.el.addClass("x-btn-click");
27086             Roo.get(document).on('mouseup', this.onMouseUp, this);
27087         }
27088     },
27089     // private
27090     onMouseUp : function(e){
27091         if(e.button == 0){
27092             this.el.removeClass("x-btn-click");
27093             Roo.get(document).un('mouseup', this.onMouseUp, this);
27094         }
27095     },
27096     // private
27097     onMenuShow : function(e){
27098         this.el.addClass("x-btn-menu-active");
27099     },
27100     // private
27101     onMenuHide : function(e){
27102         this.el.removeClass("x-btn-menu-active");
27103     }   
27104 });
27105
27106 // Private utility class used by Button
27107 Roo.ButtonToggleMgr = function(){
27108    var groups = {};
27109    
27110    function toggleGroup(btn, state){
27111        if(state){
27112            var g = groups[btn.toggleGroup];
27113            for(var i = 0, l = g.length; i < l; i++){
27114                if(g[i] != btn){
27115                    g[i].toggle(false);
27116                }
27117            }
27118        }
27119    }
27120    
27121    return {
27122        register : function(btn){
27123            if(!btn.toggleGroup){
27124                return;
27125            }
27126            var g = groups[btn.toggleGroup];
27127            if(!g){
27128                g = groups[btn.toggleGroup] = [];
27129            }
27130            g.push(btn);
27131            btn.on("toggle", toggleGroup);
27132        },
27133        
27134        unregister : function(btn){
27135            if(!btn.toggleGroup){
27136                return;
27137            }
27138            var g = groups[btn.toggleGroup];
27139            if(g){
27140                g.remove(btn);
27141                btn.un("toggle", toggleGroup);
27142            }
27143        }
27144    };
27145 }();/*
27146  * Based on:
27147  * Ext JS Library 1.1.1
27148  * Copyright(c) 2006-2007, Ext JS, LLC.
27149  *
27150  * Originally Released Under LGPL - original licence link has changed is not relivant.
27151  *
27152  * Fork - LGPL
27153  * <script type="text/javascript">
27154  */
27155  
27156 /**
27157  * @class Roo.SplitButton
27158  * @extends Roo.Button
27159  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27160  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27161  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27162  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27163  * @cfg {String} arrowTooltip The title attribute of the arrow
27164  * @constructor
27165  * Create a new menu button
27166  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27167  * @param {Object} config The config object
27168  */
27169 Roo.SplitButton = function(renderTo, config){
27170     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27171     /**
27172      * @event arrowclick
27173      * Fires when this button's arrow is clicked
27174      * @param {SplitButton} this
27175      * @param {EventObject} e The click event
27176      */
27177     this.addEvents({"arrowclick":true});
27178 };
27179
27180 Roo.extend(Roo.SplitButton, Roo.Button, {
27181     render : function(renderTo){
27182         // this is one sweet looking template!
27183         var tpl = new Roo.Template(
27184             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27185             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27186             '<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>',
27187             "</tbody></table></td><td>",
27188             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27189             '<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>',
27190             "</tbody></table></td></tr></table>"
27191         );
27192         var btn = tpl.append(renderTo, [this.text, this.type], true);
27193         var btnEl = btn.child("button");
27194         if(this.cls){
27195             btn.addClass(this.cls);
27196         }
27197         if(this.icon){
27198             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27199         }
27200         if(this.iconCls){
27201             btnEl.addClass(this.iconCls);
27202             if(!this.cls){
27203                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27204             }
27205         }
27206         this.el = btn;
27207         if(this.handleMouseEvents){
27208             btn.on("mouseover", this.onMouseOver, this);
27209             btn.on("mouseout", this.onMouseOut, this);
27210             btn.on("mousedown", this.onMouseDown, this);
27211             btn.on("mouseup", this.onMouseUp, this);
27212         }
27213         btn.on(this.clickEvent, this.onClick, this);
27214         if(this.tooltip){
27215             if(typeof this.tooltip == 'object'){
27216                 Roo.QuickTips.tips(Roo.apply({
27217                       target: btnEl.id
27218                 }, this.tooltip));
27219             } else {
27220                 btnEl.dom[this.tooltipType] = this.tooltip;
27221             }
27222         }
27223         if(this.arrowTooltip){
27224             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27225         }
27226         if(this.hidden){
27227             this.hide();
27228         }
27229         if(this.disabled){
27230             this.disable();
27231         }
27232         if(this.pressed){
27233             this.el.addClass("x-btn-pressed");
27234         }
27235         if(Roo.isIE && !Roo.isIE7){
27236             this.autoWidth.defer(1, this);
27237         }else{
27238             this.autoWidth();
27239         }
27240         if(this.menu){
27241             this.menu.on("show", this.onMenuShow, this);
27242             this.menu.on("hide", this.onMenuHide, this);
27243         }
27244         this.fireEvent('render', this);
27245     },
27246
27247     // private
27248     autoWidth : function(){
27249         if(this.el){
27250             var tbl = this.el.child("table:first");
27251             var tbl2 = this.el.child("table:last");
27252             this.el.setWidth("auto");
27253             tbl.setWidth("auto");
27254             if(Roo.isIE7 && Roo.isStrict){
27255                 var ib = this.el.child('button:first');
27256                 if(ib && ib.getWidth() > 20){
27257                     ib.clip();
27258                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27259                 }
27260             }
27261             if(this.minWidth){
27262                 if(this.hidden){
27263                     this.el.beginMeasure();
27264                 }
27265                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27266                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27267                 }
27268                 if(this.hidden){
27269                     this.el.endMeasure();
27270                 }
27271             }
27272             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27273         } 
27274     },
27275     /**
27276      * Sets this button's click handler
27277      * @param {Function} handler The function to call when the button is clicked
27278      * @param {Object} scope (optional) Scope for the function passed above
27279      */
27280     setHandler : function(handler, scope){
27281         this.handler = handler;
27282         this.scope = scope;  
27283     },
27284     
27285     /**
27286      * Sets this button's arrow click handler
27287      * @param {Function} handler The function to call when the arrow is clicked
27288      * @param {Object} scope (optional) Scope for the function passed above
27289      */
27290     setArrowHandler : function(handler, scope){
27291         this.arrowHandler = handler;
27292         this.scope = scope;  
27293     },
27294     
27295     /**
27296      * Focus the button
27297      */
27298     focus : function(){
27299         if(this.el){
27300             this.el.child("button:first").focus();
27301         }
27302     },
27303
27304     // private
27305     onClick : function(e){
27306         e.preventDefault();
27307         if(!this.disabled){
27308             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27309                 if(this.menu && !this.menu.isVisible()){
27310                     this.menu.show(this.el, this.menuAlign);
27311                 }
27312                 this.fireEvent("arrowclick", this, e);
27313                 if(this.arrowHandler){
27314                     this.arrowHandler.call(this.scope || this, this, e);
27315                 }
27316             }else{
27317                 this.fireEvent("click", this, e);
27318                 if(this.handler){
27319                     this.handler.call(this.scope || this, this, e);
27320                 }
27321             }
27322         }
27323     },
27324     // private
27325     onMouseDown : function(e){
27326         if(!this.disabled){
27327             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27328         }
27329     },
27330     // private
27331     onMouseUp : function(e){
27332         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27333     }   
27334 });
27335
27336
27337 // backwards compat
27338 Roo.MenuButton = Roo.SplitButton;/*
27339  * Based on:
27340  * Ext JS Library 1.1.1
27341  * Copyright(c) 2006-2007, Ext JS, LLC.
27342  *
27343  * Originally Released Under LGPL - original licence link has changed is not relivant.
27344  *
27345  * Fork - LGPL
27346  * <script type="text/javascript">
27347  */
27348
27349 /**
27350  * @class Roo.Toolbar
27351  * Basic Toolbar class.
27352  * @constructor
27353  * Creates a new Toolbar
27354  * @param {Object} container The config object
27355  */ 
27356 Roo.Toolbar = function(container, buttons, config)
27357 {
27358     /// old consturctor format still supported..
27359     if(container instanceof Array){ // omit the container for later rendering
27360         buttons = container;
27361         config = buttons;
27362         container = null;
27363     }
27364     if (typeof(container) == 'object' && container.xtype) {
27365         config = container;
27366         container = config.container;
27367         buttons = config.buttons || []; // not really - use items!!
27368     }
27369     var xitems = [];
27370     if (config && config.items) {
27371         xitems = config.items;
27372         delete config.items;
27373     }
27374     Roo.apply(this, config);
27375     this.buttons = buttons;
27376     
27377     if(container){
27378         this.render(container);
27379     }
27380     this.xitems = xitems;
27381     Roo.each(xitems, function(b) {
27382         this.add(b);
27383     }, this);
27384     
27385 };
27386
27387 Roo.Toolbar.prototype = {
27388     /**
27389      * @cfg {Array} items
27390      * array of button configs or elements to add (will be converted to a MixedCollection)
27391      */
27392     
27393     /**
27394      * @cfg {String/HTMLElement/Element} container
27395      * The id or element that will contain the toolbar
27396      */
27397     // private
27398     render : function(ct){
27399         this.el = Roo.get(ct);
27400         if(this.cls){
27401             this.el.addClass(this.cls);
27402         }
27403         // using a table allows for vertical alignment
27404         // 100% width is needed by Safari...
27405         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
27406         this.tr = this.el.child("tr", true);
27407         var autoId = 0;
27408         this.items = new Roo.util.MixedCollection(false, function(o){
27409             return o.id || ("item" + (++autoId));
27410         });
27411         if(this.buttons){
27412             this.add.apply(this, this.buttons);
27413             delete this.buttons;
27414         }
27415     },
27416
27417     /**
27418      * Adds element(s) to the toolbar -- this function takes a variable number of 
27419      * arguments of mixed type and adds them to the toolbar.
27420      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
27421      * <ul>
27422      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
27423      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
27424      * <li>Field: Any form field (equivalent to {@link #addField})</li>
27425      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
27426      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
27427      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
27428      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
27429      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
27430      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
27431      * </ul>
27432      * @param {Mixed} arg2
27433      * @param {Mixed} etc.
27434      */
27435     add : function(){
27436         var a = arguments, l = a.length;
27437         for(var i = 0; i < l; i++){
27438             this._add(a[i]);
27439         }
27440     },
27441     // private..
27442     _add : function(el) {
27443         
27444         if (el.xtype) {
27445             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
27446         }
27447         
27448         if (el.applyTo){ // some kind of form field
27449             return this.addField(el);
27450         } 
27451         if (el.render){ // some kind of Toolbar.Item
27452             return this.addItem(el);
27453         }
27454         if (typeof el == "string"){ // string
27455             if(el == "separator" || el == "-"){
27456                 return this.addSeparator();
27457             }
27458             if (el == " "){
27459                 return this.addSpacer();
27460             }
27461             if(el == "->"){
27462                 return this.addFill();
27463             }
27464             return this.addText(el);
27465             
27466         }
27467         if(el.tagName){ // element
27468             return this.addElement(el);
27469         }
27470         if(typeof el == "object"){ // must be button config?
27471             return this.addButton(el);
27472         }
27473         // and now what?!?!
27474         return false;
27475         
27476     },
27477     
27478     /**
27479      * Add an Xtype element
27480      * @param {Object} xtype Xtype Object
27481      * @return {Object} created Object
27482      */
27483     addxtype : function(e){
27484         return this.add(e);  
27485     },
27486     
27487     /**
27488      * Returns the Element for this toolbar.
27489      * @return {Roo.Element}
27490      */
27491     getEl : function(){
27492         return this.el;  
27493     },
27494     
27495     /**
27496      * Adds a separator
27497      * @return {Roo.Toolbar.Item} The separator item
27498      */
27499     addSeparator : function(){
27500         return this.addItem(new Roo.Toolbar.Separator());
27501     },
27502
27503     /**
27504      * Adds a spacer element
27505      * @return {Roo.Toolbar.Spacer} The spacer item
27506      */
27507     addSpacer : function(){
27508         return this.addItem(new Roo.Toolbar.Spacer());
27509     },
27510
27511     /**
27512      * Adds a fill element that forces subsequent additions to the right side of the toolbar
27513      * @return {Roo.Toolbar.Fill} The fill item
27514      */
27515     addFill : function(){
27516         return this.addItem(new Roo.Toolbar.Fill());
27517     },
27518
27519     /**
27520      * Adds any standard HTML element to the toolbar
27521      * @param {String/HTMLElement/Element} el The element or id of the element to add
27522      * @return {Roo.Toolbar.Item} The element's item
27523      */
27524     addElement : function(el){
27525         return this.addItem(new Roo.Toolbar.Item(el));
27526     },
27527     /**
27528      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
27529      * @type Roo.util.MixedCollection  
27530      */
27531     items : false,
27532      
27533     /**
27534      * Adds any Toolbar.Item or subclass
27535      * @param {Roo.Toolbar.Item} item
27536      * @return {Roo.Toolbar.Item} The item
27537      */
27538     addItem : function(item){
27539         var td = this.nextBlock();
27540         item.render(td);
27541         this.items.add(item);
27542         return item;
27543     },
27544     
27545     /**
27546      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
27547      * @param {Object/Array} config A button config or array of configs
27548      * @return {Roo.Toolbar.Button/Array}
27549      */
27550     addButton : function(config){
27551         if(config instanceof Array){
27552             var buttons = [];
27553             for(var i = 0, len = config.length; i < len; i++) {
27554                 buttons.push(this.addButton(config[i]));
27555             }
27556             return buttons;
27557         }
27558         var b = config;
27559         if(!(config instanceof Roo.Toolbar.Button)){
27560             b = config.split ?
27561                 new Roo.Toolbar.SplitButton(config) :
27562                 new Roo.Toolbar.Button(config);
27563         }
27564         var td = this.nextBlock();
27565         b.render(td);
27566         this.items.add(b);
27567         return b;
27568     },
27569     
27570     /**
27571      * Adds text to the toolbar
27572      * @param {String} text The text to add
27573      * @return {Roo.Toolbar.Item} The element's item
27574      */
27575     addText : function(text){
27576         return this.addItem(new Roo.Toolbar.TextItem(text));
27577     },
27578     
27579     /**
27580      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27581      * @param {Number} index The index where the item is to be inserted
27582      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27583      * @return {Roo.Toolbar.Button/Item}
27584      */
27585     insertButton : function(index, item){
27586         if(item instanceof Array){
27587             var buttons = [];
27588             for(var i = 0, len = item.length; i < len; i++) {
27589                buttons.push(this.insertButton(index + i, item[i]));
27590             }
27591             return buttons;
27592         }
27593         if (!(item instanceof Roo.Toolbar.Button)){
27594            item = new Roo.Toolbar.Button(item);
27595         }
27596         var td = document.createElement("td");
27597         this.tr.insertBefore(td, this.tr.childNodes[index]);
27598         item.render(td);
27599         this.items.insert(index, item);
27600         return item;
27601     },
27602     
27603     /**
27604      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27605      * @param {Object} config
27606      * @return {Roo.Toolbar.Item} The element's item
27607      */
27608     addDom : function(config, returnEl){
27609         var td = this.nextBlock();
27610         Roo.DomHelper.overwrite(td, config);
27611         var ti = new Roo.Toolbar.Item(td.firstChild);
27612         ti.render(td);
27613         this.items.add(ti);
27614         return ti;
27615     },
27616
27617     /**
27618      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27619      * @type Roo.util.MixedCollection  
27620      */
27621     fields : false,
27622     
27623     /**
27624      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27625      * Note: the field should not have been rendered yet. For a field that has already been
27626      * rendered, use {@link #addElement}.
27627      * @param {Roo.form.Field} field
27628      * @return {Roo.ToolbarItem}
27629      */
27630      
27631       
27632     addField : function(field) {
27633         if (!this.fields) {
27634             var autoId = 0;
27635             this.fields = new Roo.util.MixedCollection(false, function(o){
27636                 return o.id || ("item" + (++autoId));
27637             });
27638
27639         }
27640         
27641         var td = this.nextBlock();
27642         field.render(td);
27643         var ti = new Roo.Toolbar.Item(td.firstChild);
27644         ti.render(td);
27645         this.items.add(ti);
27646         this.fields.add(field);
27647         return ti;
27648     },
27649     /**
27650      * Hide the toolbar
27651      * @method hide
27652      */
27653      
27654       
27655     hide : function()
27656     {
27657         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27658         this.el.child('div').hide();
27659     },
27660     /**
27661      * Show the toolbar
27662      * @method show
27663      */
27664     show : function()
27665     {
27666         this.el.child('div').show();
27667     },
27668       
27669     // private
27670     nextBlock : function(){
27671         var td = document.createElement("td");
27672         this.tr.appendChild(td);
27673         return td;
27674     },
27675
27676     // private
27677     destroy : function(){
27678         if(this.items){ // rendered?
27679             Roo.destroy.apply(Roo, this.items.items);
27680         }
27681         if(this.fields){ // rendered?
27682             Roo.destroy.apply(Roo, this.fields.items);
27683         }
27684         Roo.Element.uncache(this.el, this.tr);
27685     }
27686 };
27687
27688 /**
27689  * @class Roo.Toolbar.Item
27690  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27691  * @constructor
27692  * Creates a new Item
27693  * @param {HTMLElement} el 
27694  */
27695 Roo.Toolbar.Item = function(el){
27696     this.el = Roo.getDom(el);
27697     this.id = Roo.id(this.el);
27698     this.hidden = false;
27699 };
27700
27701 Roo.Toolbar.Item.prototype = {
27702     
27703     /**
27704      * Get this item's HTML Element
27705      * @return {HTMLElement}
27706      */
27707     getEl : function(){
27708        return this.el;  
27709     },
27710
27711     // private
27712     render : function(td){
27713         this.td = td;
27714         td.appendChild(this.el);
27715     },
27716     
27717     /**
27718      * Removes and destroys this item.
27719      */
27720     destroy : function(){
27721         this.td.parentNode.removeChild(this.td);
27722     },
27723     
27724     /**
27725      * Shows this item.
27726      */
27727     show: function(){
27728         this.hidden = false;
27729         this.td.style.display = "";
27730     },
27731     
27732     /**
27733      * Hides this item.
27734      */
27735     hide: function(){
27736         this.hidden = true;
27737         this.td.style.display = "none";
27738     },
27739     
27740     /**
27741      * Convenience function for boolean show/hide.
27742      * @param {Boolean} visible true to show/false to hide
27743      */
27744     setVisible: function(visible){
27745         if(visible) {
27746             this.show();
27747         }else{
27748             this.hide();
27749         }
27750     },
27751     
27752     /**
27753      * Try to focus this item.
27754      */
27755     focus : function(){
27756         Roo.fly(this.el).focus();
27757     },
27758     
27759     /**
27760      * Disables this item.
27761      */
27762     disable : function(){
27763         Roo.fly(this.td).addClass("x-item-disabled");
27764         this.disabled = true;
27765         this.el.disabled = true;
27766     },
27767     
27768     /**
27769      * Enables this item.
27770      */
27771     enable : function(){
27772         Roo.fly(this.td).removeClass("x-item-disabled");
27773         this.disabled = false;
27774         this.el.disabled = false;
27775     }
27776 };
27777
27778
27779 /**
27780  * @class Roo.Toolbar.Separator
27781  * @extends Roo.Toolbar.Item
27782  * A simple toolbar separator class
27783  * @constructor
27784  * Creates a new Separator
27785  */
27786 Roo.Toolbar.Separator = function(){
27787     var s = document.createElement("span");
27788     s.className = "ytb-sep";
27789     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27790 };
27791 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27792     enable:Roo.emptyFn,
27793     disable:Roo.emptyFn,
27794     focus:Roo.emptyFn
27795 });
27796
27797 /**
27798  * @class Roo.Toolbar.Spacer
27799  * @extends Roo.Toolbar.Item
27800  * A simple element that adds extra horizontal space to a toolbar.
27801  * @constructor
27802  * Creates a new Spacer
27803  */
27804 Roo.Toolbar.Spacer = function(){
27805     var s = document.createElement("div");
27806     s.className = "ytb-spacer";
27807     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27808 };
27809 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27810     enable:Roo.emptyFn,
27811     disable:Roo.emptyFn,
27812     focus:Roo.emptyFn
27813 });
27814
27815 /**
27816  * @class Roo.Toolbar.Fill
27817  * @extends Roo.Toolbar.Spacer
27818  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27819  * @constructor
27820  * Creates a new Spacer
27821  */
27822 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27823     // private
27824     render : function(td){
27825         td.style.width = '100%';
27826         Roo.Toolbar.Fill.superclass.render.call(this, td);
27827     }
27828 });
27829
27830 /**
27831  * @class Roo.Toolbar.TextItem
27832  * @extends Roo.Toolbar.Item
27833  * A simple class that renders text directly into a toolbar.
27834  * @constructor
27835  * Creates a new TextItem
27836  * @param {String} text
27837  */
27838 Roo.Toolbar.TextItem = function(text){
27839     if (typeof(text) == 'object') {
27840         text = text.text;
27841     }
27842     var s = document.createElement("span");
27843     s.className = "ytb-text";
27844     s.innerHTML = text;
27845     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27846 };
27847 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27848     enable:Roo.emptyFn,
27849     disable:Roo.emptyFn,
27850     focus:Roo.emptyFn
27851 });
27852
27853 /**
27854  * @class Roo.Toolbar.Button
27855  * @extends Roo.Button
27856  * A button that renders into a toolbar.
27857  * @constructor
27858  * Creates a new Button
27859  * @param {Object} config A standard {@link Roo.Button} config object
27860  */
27861 Roo.Toolbar.Button = function(config){
27862     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27863 };
27864 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27865     render : function(td){
27866         this.td = td;
27867         Roo.Toolbar.Button.superclass.render.call(this, td);
27868     },
27869     
27870     /**
27871      * Removes and destroys this button
27872      */
27873     destroy : function(){
27874         Roo.Toolbar.Button.superclass.destroy.call(this);
27875         this.td.parentNode.removeChild(this.td);
27876     },
27877     
27878     /**
27879      * Shows this button
27880      */
27881     show: function(){
27882         this.hidden = false;
27883         this.td.style.display = "";
27884     },
27885     
27886     /**
27887      * Hides this button
27888      */
27889     hide: function(){
27890         this.hidden = true;
27891         this.td.style.display = "none";
27892     },
27893
27894     /**
27895      * Disables this item
27896      */
27897     disable : function(){
27898         Roo.fly(this.td).addClass("x-item-disabled");
27899         this.disabled = true;
27900     },
27901
27902     /**
27903      * Enables this item
27904      */
27905     enable : function(){
27906         Roo.fly(this.td).removeClass("x-item-disabled");
27907         this.disabled = false;
27908     }
27909 });
27910 // backwards compat
27911 Roo.ToolbarButton = Roo.Toolbar.Button;
27912
27913 /**
27914  * @class Roo.Toolbar.SplitButton
27915  * @extends Roo.SplitButton
27916  * A menu button that renders into a toolbar.
27917  * @constructor
27918  * Creates a new SplitButton
27919  * @param {Object} config A standard {@link Roo.SplitButton} config object
27920  */
27921 Roo.Toolbar.SplitButton = function(config){
27922     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27923 };
27924 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27925     render : function(td){
27926         this.td = td;
27927         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27928     },
27929     
27930     /**
27931      * Removes and destroys this button
27932      */
27933     destroy : function(){
27934         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27935         this.td.parentNode.removeChild(this.td);
27936     },
27937     
27938     /**
27939      * Shows this button
27940      */
27941     show: function(){
27942         this.hidden = false;
27943         this.td.style.display = "";
27944     },
27945     
27946     /**
27947      * Hides this button
27948      */
27949     hide: function(){
27950         this.hidden = true;
27951         this.td.style.display = "none";
27952     }
27953 });
27954
27955 // backwards compat
27956 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27957  * Based on:
27958  * Ext JS Library 1.1.1
27959  * Copyright(c) 2006-2007, Ext JS, LLC.
27960  *
27961  * Originally Released Under LGPL - original licence link has changed is not relivant.
27962  *
27963  * Fork - LGPL
27964  * <script type="text/javascript">
27965  */
27966  
27967 /**
27968  * @class Roo.PagingToolbar
27969  * @extends Roo.Toolbar
27970  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27971  * @constructor
27972  * Create a new PagingToolbar
27973  * @param {Object} config The config object
27974  */
27975 Roo.PagingToolbar = function(el, ds, config)
27976 {
27977     // old args format still supported... - xtype is prefered..
27978     if (typeof(el) == 'object' && el.xtype) {
27979         // created from xtype...
27980         config = el;
27981         ds = el.dataSource;
27982         el = config.container;
27983     }
27984     var items = [];
27985     if (config.items) {
27986         items = config.items;
27987         config.items = [];
27988     }
27989     
27990     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27991     this.ds = ds;
27992     this.cursor = 0;
27993     this.renderButtons(this.el);
27994     this.bind(ds);
27995     
27996     // supprot items array.
27997    
27998     Roo.each(items, function(e) {
27999         this.add(Roo.factory(e));
28000     },this);
28001     
28002 };
28003
28004 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28005     /**
28006      * @cfg {Roo.data.Store} dataSource
28007      * The underlying data store providing the paged data
28008      */
28009     /**
28010      * @cfg {String/HTMLElement/Element} container
28011      * container The id or element that will contain the toolbar
28012      */
28013     /**
28014      * @cfg {Boolean} displayInfo
28015      * True to display the displayMsg (defaults to false)
28016      */
28017     /**
28018      * @cfg {Number} pageSize
28019      * The number of records to display per page (defaults to 20)
28020      */
28021     pageSize: 20,
28022     /**
28023      * @cfg {String} displayMsg
28024      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28025      */
28026     displayMsg : 'Displaying {0} - {1} of {2}',
28027     /**
28028      * @cfg {String} emptyMsg
28029      * The message to display when no records are found (defaults to "No data to display")
28030      */
28031     emptyMsg : 'No data to display',
28032     /**
28033      * Customizable piece of the default paging text (defaults to "Page")
28034      * @type String
28035      */
28036     beforePageText : "Page",
28037     /**
28038      * Customizable piece of the default paging text (defaults to "of %0")
28039      * @type String
28040      */
28041     afterPageText : "of {0}",
28042     /**
28043      * Customizable piece of the default paging text (defaults to "First Page")
28044      * @type String
28045      */
28046     firstText : "First Page",
28047     /**
28048      * Customizable piece of the default paging text (defaults to "Previous Page")
28049      * @type String
28050      */
28051     prevText : "Previous Page",
28052     /**
28053      * Customizable piece of the default paging text (defaults to "Next Page")
28054      * @type String
28055      */
28056     nextText : "Next Page",
28057     /**
28058      * Customizable piece of the default paging text (defaults to "Last Page")
28059      * @type String
28060      */
28061     lastText : "Last Page",
28062     /**
28063      * Customizable piece of the default paging text (defaults to "Refresh")
28064      * @type String
28065      */
28066     refreshText : "Refresh",
28067
28068     // private
28069     renderButtons : function(el){
28070         Roo.PagingToolbar.superclass.render.call(this, el);
28071         this.first = this.addButton({
28072             tooltip: this.firstText,
28073             cls: "x-btn-icon x-grid-page-first",
28074             disabled: true,
28075             handler: this.onClick.createDelegate(this, ["first"])
28076         });
28077         this.prev = this.addButton({
28078             tooltip: this.prevText,
28079             cls: "x-btn-icon x-grid-page-prev",
28080             disabled: true,
28081             handler: this.onClick.createDelegate(this, ["prev"])
28082         });
28083         //this.addSeparator();
28084         this.add(this.beforePageText);
28085         this.field = Roo.get(this.addDom({
28086            tag: "input",
28087            type: "text",
28088            size: "3",
28089            value: "1",
28090            cls: "x-grid-page-number"
28091         }).el);
28092         this.field.on("keydown", this.onPagingKeydown, this);
28093         this.field.on("focus", function(){this.dom.select();});
28094         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28095         this.field.setHeight(18);
28096         //this.addSeparator();
28097         this.next = this.addButton({
28098             tooltip: this.nextText,
28099             cls: "x-btn-icon x-grid-page-next",
28100             disabled: true,
28101             handler: this.onClick.createDelegate(this, ["next"])
28102         });
28103         this.last = this.addButton({
28104             tooltip: this.lastText,
28105             cls: "x-btn-icon x-grid-page-last",
28106             disabled: true,
28107             handler: this.onClick.createDelegate(this, ["last"])
28108         });
28109         //this.addSeparator();
28110         this.loading = this.addButton({
28111             tooltip: this.refreshText,
28112             cls: "x-btn-icon x-grid-loading",
28113             handler: this.onClick.createDelegate(this, ["refresh"])
28114         });
28115
28116         if(this.displayInfo){
28117             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28118         }
28119     },
28120
28121     // private
28122     updateInfo : function(){
28123         if(this.displayEl){
28124             var count = this.ds.getCount();
28125             var msg = count == 0 ?
28126                 this.emptyMsg :
28127                 String.format(
28128                     this.displayMsg,
28129                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28130                 );
28131             this.displayEl.update(msg);
28132         }
28133     },
28134
28135     // private
28136     onLoad : function(ds, r, o){
28137        this.cursor = o.params ? o.params.start : 0;
28138        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28139
28140        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28141        this.field.dom.value = ap;
28142        this.first.setDisabled(ap == 1);
28143        this.prev.setDisabled(ap == 1);
28144        this.next.setDisabled(ap == ps);
28145        this.last.setDisabled(ap == ps);
28146        this.loading.enable();
28147        this.updateInfo();
28148     },
28149
28150     // private
28151     getPageData : function(){
28152         var total = this.ds.getTotalCount();
28153         return {
28154             total : total,
28155             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28156             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28157         };
28158     },
28159
28160     // private
28161     onLoadError : function(){
28162         this.loading.enable();
28163     },
28164
28165     // private
28166     onPagingKeydown : function(e){
28167         var k = e.getKey();
28168         var d = this.getPageData();
28169         if(k == e.RETURN){
28170             var v = this.field.dom.value, pageNum;
28171             if(!v || isNaN(pageNum = parseInt(v, 10))){
28172                 this.field.dom.value = d.activePage;
28173                 return;
28174             }
28175             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28176             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28177             e.stopEvent();
28178         }
28179         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))
28180         {
28181           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28182           this.field.dom.value = pageNum;
28183           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28184           e.stopEvent();
28185         }
28186         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28187         {
28188           var v = this.field.dom.value, pageNum; 
28189           var increment = (e.shiftKey) ? 10 : 1;
28190           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28191             increment *= -1;
28192           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28193             this.field.dom.value = d.activePage;
28194             return;
28195           }
28196           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28197           {
28198             this.field.dom.value = parseInt(v, 10) + increment;
28199             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28200             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28201           }
28202           e.stopEvent();
28203         }
28204     },
28205
28206     // private
28207     beforeLoad : function(){
28208         if(this.loading){
28209             this.loading.disable();
28210         }
28211     },
28212
28213     // private
28214     onClick : function(which){
28215         var ds = this.ds;
28216         switch(which){
28217             case "first":
28218                 ds.load({params:{start: 0, limit: this.pageSize}});
28219             break;
28220             case "prev":
28221                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28222             break;
28223             case "next":
28224                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28225             break;
28226             case "last":
28227                 var total = ds.getTotalCount();
28228                 var extra = total % this.pageSize;
28229                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28230                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28231             break;
28232             case "refresh":
28233                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28234             break;
28235         }
28236     },
28237
28238     /**
28239      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28240      * @param {Roo.data.Store} store The data store to unbind
28241      */
28242     unbind : function(ds){
28243         ds.un("beforeload", this.beforeLoad, this);
28244         ds.un("load", this.onLoad, this);
28245         ds.un("loadexception", this.onLoadError, this);
28246         ds.un("remove", this.updateInfo, this);
28247         ds.un("add", this.updateInfo, this);
28248         this.ds = undefined;
28249     },
28250
28251     /**
28252      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28253      * @param {Roo.data.Store} store The data store to bind
28254      */
28255     bind : function(ds){
28256         ds.on("beforeload", this.beforeLoad, this);
28257         ds.on("load", this.onLoad, this);
28258         ds.on("loadexception", this.onLoadError, this);
28259         ds.on("remove", this.updateInfo, this);
28260         ds.on("add", this.updateInfo, this);
28261         this.ds = ds;
28262     }
28263 });/*
28264  * Based on:
28265  * Ext JS Library 1.1.1
28266  * Copyright(c) 2006-2007, Ext JS, LLC.
28267  *
28268  * Originally Released Under LGPL - original licence link has changed is not relivant.
28269  *
28270  * Fork - LGPL
28271  * <script type="text/javascript">
28272  */
28273
28274 /**
28275  * @class Roo.Resizable
28276  * @extends Roo.util.Observable
28277  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28278  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28279  * 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
28280  * the element will be wrapped for you automatically.</p>
28281  * <p>Here is the list of valid resize handles:</p>
28282  * <pre>
28283 Value   Description
28284 ------  -------------------
28285  'n'     north
28286  's'     south
28287  'e'     east
28288  'w'     west
28289  'nw'    northwest
28290  'sw'    southwest
28291  'se'    southeast
28292  'ne'    northeast
28293  'hd'    horizontal drag
28294  'all'   all
28295 </pre>
28296  * <p>Here's an example showing the creation of a typical Resizable:</p>
28297  * <pre><code>
28298 var resizer = new Roo.Resizable("element-id", {
28299     handles: 'all',
28300     minWidth: 200,
28301     minHeight: 100,
28302     maxWidth: 500,
28303     maxHeight: 400,
28304     pinned: true
28305 });
28306 resizer.on("resize", myHandler);
28307 </code></pre>
28308  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28309  * resizer.east.setDisplayed(false);</p>
28310  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28311  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28312  * resize operation's new size (defaults to [0, 0])
28313  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28314  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28315  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28316  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28317  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28318  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28319  * @cfg {Number} width The width of the element in pixels (defaults to null)
28320  * @cfg {Number} height The height of the element in pixels (defaults to null)
28321  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28322  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28323  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28324  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28325  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28326  * in favor of the handles config option (defaults to false)
28327  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28328  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28329  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28330  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28331  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28332  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28333  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28334  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28335  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28336  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28337  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28338  * @constructor
28339  * Create a new resizable component
28340  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
28341  * @param {Object} config configuration options
28342   */
28343 Roo.Resizable = function(el, config)
28344 {
28345     this.el = Roo.get(el);
28346
28347     if(config && config.wrap){
28348         config.resizeChild = this.el;
28349         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
28350         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
28351         this.el.setStyle("overflow", "hidden");
28352         this.el.setPositioning(config.resizeChild.getPositioning());
28353         config.resizeChild.clearPositioning();
28354         if(!config.width || !config.height){
28355             var csize = config.resizeChild.getSize();
28356             this.el.setSize(csize.width, csize.height);
28357         }
28358         if(config.pinned && !config.adjustments){
28359             config.adjustments = "auto";
28360         }
28361     }
28362
28363     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
28364     this.proxy.unselectable();
28365     this.proxy.enableDisplayMode('block');
28366
28367     Roo.apply(this, config);
28368
28369     if(this.pinned){
28370         this.disableTrackOver = true;
28371         this.el.addClass("x-resizable-pinned");
28372     }
28373     // if the element isn't positioned, make it relative
28374     var position = this.el.getStyle("position");
28375     if(position != "absolute" && position != "fixed"){
28376         this.el.setStyle("position", "relative");
28377     }
28378     if(!this.handles){ // no handles passed, must be legacy style
28379         this.handles = 's,e,se';
28380         if(this.multiDirectional){
28381             this.handles += ',n,w';
28382         }
28383     }
28384     if(this.handles == "all"){
28385         this.handles = "n s e w ne nw se sw";
28386     }
28387     var hs = this.handles.split(/\s*?[,;]\s*?| /);
28388     var ps = Roo.Resizable.positions;
28389     for(var i = 0, len = hs.length; i < len; i++){
28390         if(hs[i] && ps[hs[i]]){
28391             var pos = ps[hs[i]];
28392             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
28393         }
28394     }
28395     // legacy
28396     this.corner = this.southeast;
28397     
28398     // updateBox = the box can move..
28399     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
28400         this.updateBox = true;
28401     }
28402
28403     this.activeHandle = null;
28404
28405     if(this.resizeChild){
28406         if(typeof this.resizeChild == "boolean"){
28407             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
28408         }else{
28409             this.resizeChild = Roo.get(this.resizeChild, true);
28410         }
28411     }
28412     
28413     if(this.adjustments == "auto"){
28414         var rc = this.resizeChild;
28415         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
28416         if(rc && (hw || hn)){
28417             rc.position("relative");
28418             rc.setLeft(hw ? hw.el.getWidth() : 0);
28419             rc.setTop(hn ? hn.el.getHeight() : 0);
28420         }
28421         this.adjustments = [
28422             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
28423             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
28424         ];
28425     }
28426
28427     if(this.draggable){
28428         this.dd = this.dynamic ?
28429             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
28430         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
28431     }
28432
28433     // public events
28434     this.addEvents({
28435         /**
28436          * @event beforeresize
28437          * Fired before resize is allowed. Set enabled to false to cancel resize.
28438          * @param {Roo.Resizable} this
28439          * @param {Roo.EventObject} e The mousedown event
28440          */
28441         "beforeresize" : true,
28442         /**
28443          * @event resize
28444          * Fired after a resize.
28445          * @param {Roo.Resizable} this
28446          * @param {Number} width The new width
28447          * @param {Number} height The new height
28448          * @param {Roo.EventObject} e The mouseup event
28449          */
28450         "resize" : true
28451     });
28452
28453     if(this.width !== null && this.height !== null){
28454         this.resizeTo(this.width, this.height);
28455     }else{
28456         this.updateChildSize();
28457     }
28458     if(Roo.isIE){
28459         this.el.dom.style.zoom = 1;
28460     }
28461     Roo.Resizable.superclass.constructor.call(this);
28462 };
28463
28464 Roo.extend(Roo.Resizable, Roo.util.Observable, {
28465         resizeChild : false,
28466         adjustments : [0, 0],
28467         minWidth : 5,
28468         minHeight : 5,
28469         maxWidth : 10000,
28470         maxHeight : 10000,
28471         enabled : true,
28472         animate : false,
28473         duration : .35,
28474         dynamic : false,
28475         handles : false,
28476         multiDirectional : false,
28477         disableTrackOver : false,
28478         easing : 'easeOutStrong',
28479         widthIncrement : 0,
28480         heightIncrement : 0,
28481         pinned : false,
28482         width : null,
28483         height : null,
28484         preserveRatio : false,
28485         transparent: false,
28486         minX: 0,
28487         minY: 0,
28488         draggable: false,
28489
28490         /**
28491          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
28492          */
28493         constrainTo: undefined,
28494         /**
28495          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
28496          */
28497         resizeRegion: undefined,
28498
28499
28500     /**
28501      * Perform a manual resize
28502      * @param {Number} width
28503      * @param {Number} height
28504      */
28505     resizeTo : function(width, height){
28506         this.el.setSize(width, height);
28507         this.updateChildSize();
28508         this.fireEvent("resize", this, width, height, null);
28509     },
28510
28511     // private
28512     startSizing : function(e, handle){
28513         this.fireEvent("beforeresize", this, e);
28514         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
28515
28516             if(!this.overlay){
28517                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
28518                 this.overlay.unselectable();
28519                 this.overlay.enableDisplayMode("block");
28520                 this.overlay.on("mousemove", this.onMouseMove, this);
28521                 this.overlay.on("mouseup", this.onMouseUp, this);
28522             }
28523             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
28524
28525             this.resizing = true;
28526             this.startBox = this.el.getBox();
28527             this.startPoint = e.getXY();
28528             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
28529                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
28530
28531             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28532             this.overlay.show();
28533
28534             if(this.constrainTo) {
28535                 var ct = Roo.get(this.constrainTo);
28536                 this.resizeRegion = ct.getRegion().adjust(
28537                     ct.getFrameWidth('t'),
28538                     ct.getFrameWidth('l'),
28539                     -ct.getFrameWidth('b'),
28540                     -ct.getFrameWidth('r')
28541                 );
28542             }
28543
28544             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
28545             this.proxy.show();
28546             this.proxy.setBox(this.startBox);
28547             if(!this.dynamic){
28548                 this.proxy.setStyle('visibility', 'visible');
28549             }
28550         }
28551     },
28552
28553     // private
28554     onMouseDown : function(handle, e){
28555         if(this.enabled){
28556             e.stopEvent();
28557             this.activeHandle = handle;
28558             this.startSizing(e, handle);
28559         }
28560     },
28561
28562     // private
28563     onMouseUp : function(e){
28564         var size = this.resizeElement();
28565         this.resizing = false;
28566         this.handleOut();
28567         this.overlay.hide();
28568         this.proxy.hide();
28569         this.fireEvent("resize", this, size.width, size.height, e);
28570     },
28571
28572     // private
28573     updateChildSize : function(){
28574         if(this.resizeChild){
28575             var el = this.el;
28576             var child = this.resizeChild;
28577             var adj = this.adjustments;
28578             if(el.dom.offsetWidth){
28579                 var b = el.getSize(true);
28580                 child.setSize(b.width+adj[0], b.height+adj[1]);
28581             }
28582             // Second call here for IE
28583             // The first call enables instant resizing and
28584             // the second call corrects scroll bars if they
28585             // exist
28586             if(Roo.isIE){
28587                 setTimeout(function(){
28588                     if(el.dom.offsetWidth){
28589                         var b = el.getSize(true);
28590                         child.setSize(b.width+adj[0], b.height+adj[1]);
28591                     }
28592                 }, 10);
28593             }
28594         }
28595     },
28596
28597     // private
28598     snap : function(value, inc, min){
28599         if(!inc || !value) return value;
28600         var newValue = value;
28601         var m = value % inc;
28602         if(m > 0){
28603             if(m > (inc/2)){
28604                 newValue = value + (inc-m);
28605             }else{
28606                 newValue = value - m;
28607             }
28608         }
28609         return Math.max(min, newValue);
28610     },
28611
28612     // private
28613     resizeElement : function(){
28614         var box = this.proxy.getBox();
28615         if(this.updateBox){
28616             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28617         }else{
28618             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28619         }
28620         this.updateChildSize();
28621         if(!this.dynamic){
28622             this.proxy.hide();
28623         }
28624         return box;
28625     },
28626
28627     // private
28628     constrain : function(v, diff, m, mx){
28629         if(v - diff < m){
28630             diff = v - m;
28631         }else if(v - diff > mx){
28632             diff = mx - v;
28633         }
28634         return diff;
28635     },
28636
28637     // private
28638     onMouseMove : function(e){
28639         if(this.enabled){
28640             try{// try catch so if something goes wrong the user doesn't get hung
28641
28642             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28643                 return;
28644             }
28645
28646             //var curXY = this.startPoint;
28647             var curSize = this.curSize || this.startBox;
28648             var x = this.startBox.x, y = this.startBox.y;
28649             var ox = x, oy = y;
28650             var w = curSize.width, h = curSize.height;
28651             var ow = w, oh = h;
28652             var mw = this.minWidth, mh = this.minHeight;
28653             var mxw = this.maxWidth, mxh = this.maxHeight;
28654             var wi = this.widthIncrement;
28655             var hi = this.heightIncrement;
28656
28657             var eventXY = e.getXY();
28658             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28659             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28660
28661             var pos = this.activeHandle.position;
28662
28663             switch(pos){
28664                 case "east":
28665                     w += diffX;
28666                     w = Math.min(Math.max(mw, w), mxw);
28667                     break;
28668              
28669                 case "south":
28670                     h += diffY;
28671                     h = Math.min(Math.max(mh, h), mxh);
28672                     break;
28673                 case "southeast":
28674                     w += diffX;
28675                     h += diffY;
28676                     w = Math.min(Math.max(mw, w), mxw);
28677                     h = Math.min(Math.max(mh, h), mxh);
28678                     break;
28679                 case "north":
28680                     diffY = this.constrain(h, diffY, mh, mxh);
28681                     y += diffY;
28682                     h -= diffY;
28683                     break;
28684                 case "hdrag":
28685                     
28686                     if (wi) {
28687                         var adiffX = Math.abs(diffX);
28688                         var sub = (adiffX % wi); // how much 
28689                         if (sub > (wi/2)) { // far enough to snap
28690                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28691                         } else {
28692                             // remove difference.. 
28693                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28694                         }
28695                     }
28696                     x += diffX;
28697                     x = Math.max(this.minX, x);
28698                     break;
28699                 case "west":
28700                     diffX = this.constrain(w, diffX, mw, mxw);
28701                     x += diffX;
28702                     w -= diffX;
28703                     break;
28704                 case "northeast":
28705                     w += diffX;
28706                     w = Math.min(Math.max(mw, w), mxw);
28707                     diffY = this.constrain(h, diffY, mh, mxh);
28708                     y += diffY;
28709                     h -= diffY;
28710                     break;
28711                 case "northwest":
28712                     diffX = this.constrain(w, diffX, mw, mxw);
28713                     diffY = this.constrain(h, diffY, mh, mxh);
28714                     y += diffY;
28715                     h -= diffY;
28716                     x += diffX;
28717                     w -= diffX;
28718                     break;
28719                case "southwest":
28720                     diffX = this.constrain(w, diffX, mw, mxw);
28721                     h += diffY;
28722                     h = Math.min(Math.max(mh, h), mxh);
28723                     x += diffX;
28724                     w -= diffX;
28725                     break;
28726             }
28727
28728             var sw = this.snap(w, wi, mw);
28729             var sh = this.snap(h, hi, mh);
28730             if(sw != w || sh != h){
28731                 switch(pos){
28732                     case "northeast":
28733                         y -= sh - h;
28734                     break;
28735                     case "north":
28736                         y -= sh - h;
28737                         break;
28738                     case "southwest":
28739                         x -= sw - w;
28740                     break;
28741                     case "west":
28742                         x -= sw - w;
28743                         break;
28744                     case "northwest":
28745                         x -= sw - w;
28746                         y -= sh - h;
28747                     break;
28748                 }
28749                 w = sw;
28750                 h = sh;
28751             }
28752
28753             if(this.preserveRatio){
28754                 switch(pos){
28755                     case "southeast":
28756                     case "east":
28757                         h = oh * (w/ow);
28758                         h = Math.min(Math.max(mh, h), mxh);
28759                         w = ow * (h/oh);
28760                        break;
28761                     case "south":
28762                         w = ow * (h/oh);
28763                         w = Math.min(Math.max(mw, w), mxw);
28764                         h = oh * (w/ow);
28765                         break;
28766                     case "northeast":
28767                         w = ow * (h/oh);
28768                         w = Math.min(Math.max(mw, w), mxw);
28769                         h = oh * (w/ow);
28770                     break;
28771                     case "north":
28772                         var tw = w;
28773                         w = ow * (h/oh);
28774                         w = Math.min(Math.max(mw, w), mxw);
28775                         h = oh * (w/ow);
28776                         x += (tw - w) / 2;
28777                         break;
28778                     case "southwest":
28779                         h = oh * (w/ow);
28780                         h = Math.min(Math.max(mh, h), mxh);
28781                         var tw = w;
28782                         w = ow * (h/oh);
28783                         x += tw - w;
28784                         break;
28785                     case "west":
28786                         var th = h;
28787                         h = oh * (w/ow);
28788                         h = Math.min(Math.max(mh, h), mxh);
28789                         y += (th - h) / 2;
28790                         var tw = w;
28791                         w = ow * (h/oh);
28792                         x += tw - w;
28793                        break;
28794                     case "northwest":
28795                         var tw = w;
28796                         var th = h;
28797                         h = oh * (w/ow);
28798                         h = Math.min(Math.max(mh, h), mxh);
28799                         w = ow * (h/oh);
28800                         y += th - h;
28801                         x += tw - w;
28802                        break;
28803
28804                 }
28805             }
28806             if (pos == 'hdrag') {
28807                 w = ow;
28808             }
28809             this.proxy.setBounds(x, y, w, h);
28810             if(this.dynamic){
28811                 this.resizeElement();
28812             }
28813             }catch(e){}
28814         }
28815     },
28816
28817     // private
28818     handleOver : function(){
28819         if(this.enabled){
28820             this.el.addClass("x-resizable-over");
28821         }
28822     },
28823
28824     // private
28825     handleOut : function(){
28826         if(!this.resizing){
28827             this.el.removeClass("x-resizable-over");
28828         }
28829     },
28830
28831     /**
28832      * Returns the element this component is bound to.
28833      * @return {Roo.Element}
28834      */
28835     getEl : function(){
28836         return this.el;
28837     },
28838
28839     /**
28840      * Returns the resizeChild element (or null).
28841      * @return {Roo.Element}
28842      */
28843     getResizeChild : function(){
28844         return this.resizeChild;
28845     },
28846
28847     /**
28848      * Destroys this resizable. If the element was wrapped and
28849      * removeEl is not true then the element remains.
28850      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28851      */
28852     destroy : function(removeEl){
28853         this.proxy.remove();
28854         if(this.overlay){
28855             this.overlay.removeAllListeners();
28856             this.overlay.remove();
28857         }
28858         var ps = Roo.Resizable.positions;
28859         for(var k in ps){
28860             if(typeof ps[k] != "function" && this[ps[k]]){
28861                 var h = this[ps[k]];
28862                 h.el.removeAllListeners();
28863                 h.el.remove();
28864             }
28865         }
28866         if(removeEl){
28867             this.el.update("");
28868             this.el.remove();
28869         }
28870     }
28871 });
28872
28873 // private
28874 // hash to map config positions to true positions
28875 Roo.Resizable.positions = {
28876     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28877     hd: "hdrag"
28878 };
28879
28880 // private
28881 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28882     if(!this.tpl){
28883         // only initialize the template if resizable is used
28884         var tpl = Roo.DomHelper.createTemplate(
28885             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28886         );
28887         tpl.compile();
28888         Roo.Resizable.Handle.prototype.tpl = tpl;
28889     }
28890     this.position = pos;
28891     this.rz = rz;
28892     // show north drag fro topdra
28893     var handlepos = pos == 'hdrag' ? 'north' : pos;
28894     
28895     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28896     if (pos == 'hdrag') {
28897         this.el.setStyle('cursor', 'pointer');
28898     }
28899     this.el.unselectable();
28900     if(transparent){
28901         this.el.setOpacity(0);
28902     }
28903     this.el.on("mousedown", this.onMouseDown, this);
28904     if(!disableTrackOver){
28905         this.el.on("mouseover", this.onMouseOver, this);
28906         this.el.on("mouseout", this.onMouseOut, this);
28907     }
28908 };
28909
28910 // private
28911 Roo.Resizable.Handle.prototype = {
28912     afterResize : function(rz){
28913         // do nothing
28914     },
28915     // private
28916     onMouseDown : function(e){
28917         this.rz.onMouseDown(this, e);
28918     },
28919     // private
28920     onMouseOver : function(e){
28921         this.rz.handleOver(this, e);
28922     },
28923     // private
28924     onMouseOut : function(e){
28925         this.rz.handleOut(this, e);
28926     }
28927 };/*
28928  * Based on:
28929  * Ext JS Library 1.1.1
28930  * Copyright(c) 2006-2007, Ext JS, LLC.
28931  *
28932  * Originally Released Under LGPL - original licence link has changed is not relivant.
28933  *
28934  * Fork - LGPL
28935  * <script type="text/javascript">
28936  */
28937
28938 /**
28939  * @class Roo.Editor
28940  * @extends Roo.Component
28941  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28942  * @constructor
28943  * Create a new Editor
28944  * @param {Roo.form.Field} field The Field object (or descendant)
28945  * @param {Object} config The config object
28946  */
28947 Roo.Editor = function(field, config){
28948     Roo.Editor.superclass.constructor.call(this, config);
28949     this.field = field;
28950     this.addEvents({
28951         /**
28952              * @event beforestartedit
28953              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28954              * false from the handler of this event.
28955              * @param {Editor} this
28956              * @param {Roo.Element} boundEl The underlying element bound to this editor
28957              * @param {Mixed} value The field value being set
28958              */
28959         "beforestartedit" : true,
28960         /**
28961              * @event startedit
28962              * Fires when this editor is displayed
28963              * @param {Roo.Element} boundEl The underlying element bound to this editor
28964              * @param {Mixed} value The starting field value
28965              */
28966         "startedit" : true,
28967         /**
28968              * @event beforecomplete
28969              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28970              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28971              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28972              * event will not fire since no edit actually occurred.
28973              * @param {Editor} this
28974              * @param {Mixed} value The current field value
28975              * @param {Mixed} startValue The original field value
28976              */
28977         "beforecomplete" : true,
28978         /**
28979              * @event complete
28980              * Fires after editing is complete and any changed value has been written to the underlying field.
28981              * @param {Editor} this
28982              * @param {Mixed} value The current field value
28983              * @param {Mixed} startValue The original field value
28984              */
28985         "complete" : true,
28986         /**
28987          * @event specialkey
28988          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28989          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28990          * @param {Roo.form.Field} this
28991          * @param {Roo.EventObject} e The event object
28992          */
28993         "specialkey" : true
28994     });
28995 };
28996
28997 Roo.extend(Roo.Editor, Roo.Component, {
28998     /**
28999      * @cfg {Boolean/String} autosize
29000      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29001      * or "height" to adopt the height only (defaults to false)
29002      */
29003     /**
29004      * @cfg {Boolean} revertInvalid
29005      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29006      * validation fails (defaults to true)
29007      */
29008     /**
29009      * @cfg {Boolean} ignoreNoChange
29010      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29011      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29012      * will never be ignored.
29013      */
29014     /**
29015      * @cfg {Boolean} hideEl
29016      * False to keep the bound element visible while the editor is displayed (defaults to true)
29017      */
29018     /**
29019      * @cfg {Mixed} value
29020      * The data value of the underlying field (defaults to "")
29021      */
29022     value : "",
29023     /**
29024      * @cfg {String} alignment
29025      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29026      */
29027     alignment: "c-c?",
29028     /**
29029      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29030      * for bottom-right shadow (defaults to "frame")
29031      */
29032     shadow : "frame",
29033     /**
29034      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29035      */
29036     constrain : false,
29037     /**
29038      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29039      */
29040     completeOnEnter : false,
29041     /**
29042      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29043      */
29044     cancelOnEsc : false,
29045     /**
29046      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29047      */
29048     updateEl : false,
29049
29050     // private
29051     onRender : function(ct, position){
29052         this.el = new Roo.Layer({
29053             shadow: this.shadow,
29054             cls: "x-editor",
29055             parentEl : ct,
29056             shim : this.shim,
29057             shadowOffset:4,
29058             id: this.id,
29059             constrain: this.constrain
29060         });
29061         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29062         if(this.field.msgTarget != 'title'){
29063             this.field.msgTarget = 'qtip';
29064         }
29065         this.field.render(this.el);
29066         if(Roo.isGecko){
29067             this.field.el.dom.setAttribute('autocomplete', 'off');
29068         }
29069         this.field.on("specialkey", this.onSpecialKey, this);
29070         if(this.swallowKeys){
29071             this.field.el.swallowEvent(['keydown','keypress']);
29072         }
29073         this.field.show();
29074         this.field.on("blur", this.onBlur, this);
29075         if(this.field.grow){
29076             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29077         }
29078     },
29079
29080     onSpecialKey : function(field, e)
29081     {
29082         //Roo.log('editor onSpecialKey');
29083         if(this.completeOnEnter && e.getKey() == e.ENTER){
29084             e.stopEvent();
29085             this.completeEdit();
29086             return;
29087         }
29088         // do not fire special key otherwise it might hide close the editor...
29089         if(e.getKey() == e.ENTER){    
29090             return;
29091         }
29092         if(this.cancelOnEsc && e.getKey() == e.ESC){
29093             this.cancelEdit();
29094             return;
29095         } 
29096         this.fireEvent('specialkey', field, e);
29097     
29098     },
29099
29100     /**
29101      * Starts the editing process and shows the editor.
29102      * @param {String/HTMLElement/Element} el The element to edit
29103      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29104       * to the innerHTML of el.
29105      */
29106     startEdit : function(el, value){
29107         if(this.editing){
29108             this.completeEdit();
29109         }
29110         this.boundEl = Roo.get(el);
29111         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29112         if(!this.rendered){
29113             this.render(this.parentEl || document.body);
29114         }
29115         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29116             return;
29117         }
29118         this.startValue = v;
29119         this.field.setValue(v);
29120         if(this.autoSize){
29121             var sz = this.boundEl.getSize();
29122             switch(this.autoSize){
29123                 case "width":
29124                 this.setSize(sz.width,  "");
29125                 break;
29126                 case "height":
29127                 this.setSize("",  sz.height);
29128                 break;
29129                 default:
29130                 this.setSize(sz.width,  sz.height);
29131             }
29132         }
29133         this.el.alignTo(this.boundEl, this.alignment);
29134         this.editing = true;
29135         if(Roo.QuickTips){
29136             Roo.QuickTips.disable();
29137         }
29138         this.show();
29139     },
29140
29141     /**
29142      * Sets the height and width of this editor.
29143      * @param {Number} width The new width
29144      * @param {Number} height The new height
29145      */
29146     setSize : function(w, h){
29147         this.field.setSize(w, h);
29148         if(this.el){
29149             this.el.sync();
29150         }
29151     },
29152
29153     /**
29154      * Realigns the editor to the bound field based on the current alignment config value.
29155      */
29156     realign : function(){
29157         this.el.alignTo(this.boundEl, this.alignment);
29158     },
29159
29160     /**
29161      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29162      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29163      */
29164     completeEdit : function(remainVisible){
29165         if(!this.editing){
29166             return;
29167         }
29168         var v = this.getValue();
29169         if(this.revertInvalid !== false && !this.field.isValid()){
29170             v = this.startValue;
29171             this.cancelEdit(true);
29172         }
29173         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29174             this.editing = false;
29175             this.hide();
29176             return;
29177         }
29178         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29179             this.editing = false;
29180             if(this.updateEl && this.boundEl){
29181                 this.boundEl.update(v);
29182             }
29183             if(remainVisible !== true){
29184                 this.hide();
29185             }
29186             this.fireEvent("complete", this, v, this.startValue);
29187         }
29188     },
29189
29190     // private
29191     onShow : function(){
29192         this.el.show();
29193         if(this.hideEl !== false){
29194             this.boundEl.hide();
29195         }
29196         this.field.show();
29197         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29198             this.fixIEFocus = true;
29199             this.deferredFocus.defer(50, this);
29200         }else{
29201             this.field.focus();
29202         }
29203         this.fireEvent("startedit", this.boundEl, this.startValue);
29204     },
29205
29206     deferredFocus : function(){
29207         if(this.editing){
29208             this.field.focus();
29209         }
29210     },
29211
29212     /**
29213      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29214      * reverted to the original starting value.
29215      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29216      * cancel (defaults to false)
29217      */
29218     cancelEdit : function(remainVisible){
29219         if(this.editing){
29220             this.setValue(this.startValue);
29221             if(remainVisible !== true){
29222                 this.hide();
29223             }
29224         }
29225     },
29226
29227     // private
29228     onBlur : function(){
29229         if(this.allowBlur !== true && this.editing){
29230             this.completeEdit();
29231         }
29232     },
29233
29234     // private
29235     onHide : function(){
29236         if(this.editing){
29237             this.completeEdit();
29238             return;
29239         }
29240         this.field.blur();
29241         if(this.field.collapse){
29242             this.field.collapse();
29243         }
29244         this.el.hide();
29245         if(this.hideEl !== false){
29246             this.boundEl.show();
29247         }
29248         if(Roo.QuickTips){
29249             Roo.QuickTips.enable();
29250         }
29251     },
29252
29253     /**
29254      * Sets the data value of the editor
29255      * @param {Mixed} value Any valid value supported by the underlying field
29256      */
29257     setValue : function(v){
29258         this.field.setValue(v);
29259     },
29260
29261     /**
29262      * Gets the data value of the editor
29263      * @return {Mixed} The data value
29264      */
29265     getValue : function(){
29266         return this.field.getValue();
29267     }
29268 });/*
29269  * Based on:
29270  * Ext JS Library 1.1.1
29271  * Copyright(c) 2006-2007, Ext JS, LLC.
29272  *
29273  * Originally Released Under LGPL - original licence link has changed is not relivant.
29274  *
29275  * Fork - LGPL
29276  * <script type="text/javascript">
29277  */
29278  
29279 /**
29280  * @class Roo.BasicDialog
29281  * @extends Roo.util.Observable
29282  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29283  * <pre><code>
29284 var dlg = new Roo.BasicDialog("my-dlg", {
29285     height: 200,
29286     width: 300,
29287     minHeight: 100,
29288     minWidth: 150,
29289     modal: true,
29290     proxyDrag: true,
29291     shadow: true
29292 });
29293 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29294 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29295 dlg.addButton('Cancel', dlg.hide, dlg);
29296 dlg.show();
29297 </code></pre>
29298   <b>A Dialog should always be a direct child of the body element.</b>
29299  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29300  * @cfg {String} title Default text to display in the title bar (defaults to null)
29301  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29302  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29303  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29304  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29305  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29306  * (defaults to null with no animation)
29307  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29308  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29309  * property for valid values (defaults to 'all')
29310  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29311  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29312  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29313  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29314  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29315  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29316  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29317  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29318  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29319  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29320  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29321  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
29322  * draggable = true (defaults to false)
29323  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
29324  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29325  * shadow (defaults to false)
29326  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
29327  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
29328  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
29329  * @cfg {Array} buttons Array of buttons
29330  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
29331  * @constructor
29332  * Create a new BasicDialog.
29333  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
29334  * @param {Object} config Configuration options
29335  */
29336 Roo.BasicDialog = function(el, config){
29337     this.el = Roo.get(el);
29338     var dh = Roo.DomHelper;
29339     if(!this.el && config && config.autoCreate){
29340         if(typeof config.autoCreate == "object"){
29341             if(!config.autoCreate.id){
29342                 config.autoCreate.id = el;
29343             }
29344             this.el = dh.append(document.body,
29345                         config.autoCreate, true);
29346         }else{
29347             this.el = dh.append(document.body,
29348                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
29349         }
29350     }
29351     el = this.el;
29352     el.setDisplayed(true);
29353     el.hide = this.hideAction;
29354     this.id = el.id;
29355     el.addClass("x-dlg");
29356
29357     Roo.apply(this, config);
29358
29359     this.proxy = el.createProxy("x-dlg-proxy");
29360     this.proxy.hide = this.hideAction;
29361     this.proxy.setOpacity(.5);
29362     this.proxy.hide();
29363
29364     if(config.width){
29365         el.setWidth(config.width);
29366     }
29367     if(config.height){
29368         el.setHeight(config.height);
29369     }
29370     this.size = el.getSize();
29371     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
29372         this.xy = [config.x,config.y];
29373     }else{
29374         this.xy = el.getCenterXY(true);
29375     }
29376     /** The header element @type Roo.Element */
29377     this.header = el.child("> .x-dlg-hd");
29378     /** The body element @type Roo.Element */
29379     this.body = el.child("> .x-dlg-bd");
29380     /** The footer element @type Roo.Element */
29381     this.footer = el.child("> .x-dlg-ft");
29382
29383     if(!this.header){
29384         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
29385     }
29386     if(!this.body){
29387         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
29388     }
29389
29390     this.header.unselectable();
29391     if(this.title){
29392         this.header.update(this.title);
29393     }
29394     // this element allows the dialog to be focused for keyboard event
29395     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
29396     this.focusEl.swallowEvent("click", true);
29397
29398     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
29399
29400     // wrap the body and footer for special rendering
29401     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
29402     if(this.footer){
29403         this.bwrap.dom.appendChild(this.footer.dom);
29404     }
29405
29406     this.bg = this.el.createChild({
29407         tag: "div", cls:"x-dlg-bg",
29408         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
29409     });
29410     this.centerBg = this.bg.child("div.x-dlg-bg-center");
29411
29412
29413     if(this.autoScroll !== false && !this.autoTabs){
29414         this.body.setStyle("overflow", "auto");
29415     }
29416
29417     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
29418
29419     if(this.closable !== false){
29420         this.el.addClass("x-dlg-closable");
29421         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
29422         this.close.on("click", this.closeClick, this);
29423         this.close.addClassOnOver("x-dlg-close-over");
29424     }
29425     if(this.collapsible !== false){
29426         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
29427         this.collapseBtn.on("click", this.collapseClick, this);
29428         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
29429         this.header.on("dblclick", this.collapseClick, this);
29430     }
29431     if(this.resizable !== false){
29432         this.el.addClass("x-dlg-resizable");
29433         this.resizer = new Roo.Resizable(el, {
29434             minWidth: this.minWidth || 80,
29435             minHeight:this.minHeight || 80,
29436             handles: this.resizeHandles || "all",
29437             pinned: true
29438         });
29439         this.resizer.on("beforeresize", this.beforeResize, this);
29440         this.resizer.on("resize", this.onResize, this);
29441     }
29442     if(this.draggable !== false){
29443         el.addClass("x-dlg-draggable");
29444         if (!this.proxyDrag) {
29445             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
29446         }
29447         else {
29448             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
29449         }
29450         dd.setHandleElId(this.header.id);
29451         dd.endDrag = this.endMove.createDelegate(this);
29452         dd.startDrag = this.startMove.createDelegate(this);
29453         dd.onDrag = this.onDrag.createDelegate(this);
29454         dd.scroll = false;
29455         this.dd = dd;
29456     }
29457     if(this.modal){
29458         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
29459         this.mask.enableDisplayMode("block");
29460         this.mask.hide();
29461         this.el.addClass("x-dlg-modal");
29462     }
29463     if(this.shadow){
29464         this.shadow = new Roo.Shadow({
29465             mode : typeof this.shadow == "string" ? this.shadow : "sides",
29466             offset : this.shadowOffset
29467         });
29468     }else{
29469         this.shadowOffset = 0;
29470     }
29471     if(Roo.useShims && this.shim !== false){
29472         this.shim = this.el.createShim();
29473         this.shim.hide = this.hideAction;
29474         this.shim.hide();
29475     }else{
29476         this.shim = false;
29477     }
29478     if(this.autoTabs){
29479         this.initTabs();
29480     }
29481     if (this.buttons) { 
29482         var bts= this.buttons;
29483         this.buttons = [];
29484         Roo.each(bts, function(b) {
29485             this.addButton(b);
29486         }, this);
29487     }
29488     
29489     
29490     this.addEvents({
29491         /**
29492          * @event keydown
29493          * Fires when a key is pressed
29494          * @param {Roo.BasicDialog} this
29495          * @param {Roo.EventObject} e
29496          */
29497         "keydown" : true,
29498         /**
29499          * @event move
29500          * Fires when this dialog is moved by the user.
29501          * @param {Roo.BasicDialog} this
29502          * @param {Number} x The new page X
29503          * @param {Number} y The new page Y
29504          */
29505         "move" : true,
29506         /**
29507          * @event resize
29508          * Fires when this dialog is resized by the user.
29509          * @param {Roo.BasicDialog} this
29510          * @param {Number} width The new width
29511          * @param {Number} height The new height
29512          */
29513         "resize" : true,
29514         /**
29515          * @event beforehide
29516          * Fires before this dialog is hidden.
29517          * @param {Roo.BasicDialog} this
29518          */
29519         "beforehide" : true,
29520         /**
29521          * @event hide
29522          * Fires when this dialog is hidden.
29523          * @param {Roo.BasicDialog} this
29524          */
29525         "hide" : true,
29526         /**
29527          * @event beforeshow
29528          * Fires before this dialog is shown.
29529          * @param {Roo.BasicDialog} this
29530          */
29531         "beforeshow" : true,
29532         /**
29533          * @event show
29534          * Fires when this dialog is shown.
29535          * @param {Roo.BasicDialog} this
29536          */
29537         "show" : true
29538     });
29539     el.on("keydown", this.onKeyDown, this);
29540     el.on("mousedown", this.toFront, this);
29541     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
29542     this.el.hide();
29543     Roo.DialogManager.register(this);
29544     Roo.BasicDialog.superclass.constructor.call(this);
29545 };
29546
29547 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29548     shadowOffset: Roo.isIE ? 6 : 5,
29549     minHeight: 80,
29550     minWidth: 200,
29551     minButtonWidth: 75,
29552     defaultButton: null,
29553     buttonAlign: "right",
29554     tabTag: 'div',
29555     firstShow: true,
29556
29557     /**
29558      * Sets the dialog title text
29559      * @param {String} text The title text to display
29560      * @return {Roo.BasicDialog} this
29561      */
29562     setTitle : function(text){
29563         this.header.update(text);
29564         return this;
29565     },
29566
29567     // private
29568     closeClick : function(){
29569         this.hide();
29570     },
29571
29572     // private
29573     collapseClick : function(){
29574         this[this.collapsed ? "expand" : "collapse"]();
29575     },
29576
29577     /**
29578      * Collapses the dialog to its minimized state (only the title bar is visible).
29579      * Equivalent to the user clicking the collapse dialog button.
29580      */
29581     collapse : function(){
29582         if(!this.collapsed){
29583             this.collapsed = true;
29584             this.el.addClass("x-dlg-collapsed");
29585             this.restoreHeight = this.el.getHeight();
29586             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29587         }
29588     },
29589
29590     /**
29591      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29592      * clicking the expand dialog button.
29593      */
29594     expand : function(){
29595         if(this.collapsed){
29596             this.collapsed = false;
29597             this.el.removeClass("x-dlg-collapsed");
29598             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29599         }
29600     },
29601
29602     /**
29603      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29604      * @return {Roo.TabPanel} The tabs component
29605      */
29606     initTabs : function(){
29607         var tabs = this.getTabs();
29608         while(tabs.getTab(0)){
29609             tabs.removeTab(0);
29610         }
29611         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29612             var dom = el.dom;
29613             tabs.addTab(Roo.id(dom), dom.title);
29614             dom.title = "";
29615         });
29616         tabs.activate(0);
29617         return tabs;
29618     },
29619
29620     // private
29621     beforeResize : function(){
29622         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29623     },
29624
29625     // private
29626     onResize : function(){
29627         this.refreshSize();
29628         this.syncBodyHeight();
29629         this.adjustAssets();
29630         this.focus();
29631         this.fireEvent("resize", this, this.size.width, this.size.height);
29632     },
29633
29634     // private
29635     onKeyDown : function(e){
29636         if(this.isVisible()){
29637             this.fireEvent("keydown", this, e);
29638         }
29639     },
29640
29641     /**
29642      * Resizes the dialog.
29643      * @param {Number} width
29644      * @param {Number} height
29645      * @return {Roo.BasicDialog} this
29646      */
29647     resizeTo : function(width, height){
29648         this.el.setSize(width, height);
29649         this.size = {width: width, height: height};
29650         this.syncBodyHeight();
29651         if(this.fixedcenter){
29652             this.center();
29653         }
29654         if(this.isVisible()){
29655             this.constrainXY();
29656             this.adjustAssets();
29657         }
29658         this.fireEvent("resize", this, width, height);
29659         return this;
29660     },
29661
29662
29663     /**
29664      * Resizes the dialog to fit the specified content size.
29665      * @param {Number} width
29666      * @param {Number} height
29667      * @return {Roo.BasicDialog} this
29668      */
29669     setContentSize : function(w, h){
29670         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29671         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29672         //if(!this.el.isBorderBox()){
29673             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29674             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29675         //}
29676         if(this.tabs){
29677             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29678             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29679         }
29680         this.resizeTo(w, h);
29681         return this;
29682     },
29683
29684     /**
29685      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29686      * executed in response to a particular key being pressed while the dialog is active.
29687      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29688      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29689      * @param {Function} fn The function to call
29690      * @param {Object} scope (optional) The scope of the function
29691      * @return {Roo.BasicDialog} this
29692      */
29693     addKeyListener : function(key, fn, scope){
29694         var keyCode, shift, ctrl, alt;
29695         if(typeof key == "object" && !(key instanceof Array)){
29696             keyCode = key["key"];
29697             shift = key["shift"];
29698             ctrl = key["ctrl"];
29699             alt = key["alt"];
29700         }else{
29701             keyCode = key;
29702         }
29703         var handler = function(dlg, e){
29704             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29705                 var k = e.getKey();
29706                 if(keyCode instanceof Array){
29707                     for(var i = 0, len = keyCode.length; i < len; i++){
29708                         if(keyCode[i] == k){
29709                           fn.call(scope || window, dlg, k, e);
29710                           return;
29711                         }
29712                     }
29713                 }else{
29714                     if(k == keyCode){
29715                         fn.call(scope || window, dlg, k, e);
29716                     }
29717                 }
29718             }
29719         };
29720         this.on("keydown", handler);
29721         return this;
29722     },
29723
29724     /**
29725      * Returns the TabPanel component (creates it if it doesn't exist).
29726      * Note: If you wish to simply check for the existence of tabs without creating them,
29727      * check for a null 'tabs' property.
29728      * @return {Roo.TabPanel} The tabs component
29729      */
29730     getTabs : function(){
29731         if(!this.tabs){
29732             this.el.addClass("x-dlg-auto-tabs");
29733             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29734             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29735         }
29736         return this.tabs;
29737     },
29738
29739     /**
29740      * Adds a button to the footer section of the dialog.
29741      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29742      * object or a valid Roo.DomHelper element config
29743      * @param {Function} handler The function called when the button is clicked
29744      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29745      * @return {Roo.Button} The new button
29746      */
29747     addButton : function(config, handler, scope){
29748         var dh = Roo.DomHelper;
29749         if(!this.footer){
29750             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29751         }
29752         if(!this.btnContainer){
29753             var tb = this.footer.createChild({
29754
29755                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29756                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29757             }, null, true);
29758             this.btnContainer = tb.firstChild.firstChild.firstChild;
29759         }
29760         var bconfig = {
29761             handler: handler,
29762             scope: scope,
29763             minWidth: this.minButtonWidth,
29764             hideParent:true
29765         };
29766         if(typeof config == "string"){
29767             bconfig.text = config;
29768         }else{
29769             if(config.tag){
29770                 bconfig.dhconfig = config;
29771             }else{
29772                 Roo.apply(bconfig, config);
29773             }
29774         }
29775         var fc = false;
29776         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29777             bconfig.position = Math.max(0, bconfig.position);
29778             fc = this.btnContainer.childNodes[bconfig.position];
29779         }
29780          
29781         var btn = new Roo.Button(
29782             fc ? 
29783                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29784                 : this.btnContainer.appendChild(document.createElement("td")),
29785             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29786             bconfig
29787         );
29788         this.syncBodyHeight();
29789         if(!this.buttons){
29790             /**
29791              * Array of all the buttons that have been added to this dialog via addButton
29792              * @type Array
29793              */
29794             this.buttons = [];
29795         }
29796         this.buttons.push(btn);
29797         return btn;
29798     },
29799
29800     /**
29801      * Sets the default button to be focused when the dialog is displayed.
29802      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29803      * @return {Roo.BasicDialog} this
29804      */
29805     setDefaultButton : function(btn){
29806         this.defaultButton = btn;
29807         return this;
29808     },
29809
29810     // private
29811     getHeaderFooterHeight : function(safe){
29812         var height = 0;
29813         if(this.header){
29814            height += this.header.getHeight();
29815         }
29816         if(this.footer){
29817            var fm = this.footer.getMargins();
29818             height += (this.footer.getHeight()+fm.top+fm.bottom);
29819         }
29820         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29821         height += this.centerBg.getPadding("tb");
29822         return height;
29823     },
29824
29825     // private
29826     syncBodyHeight : function(){
29827         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29828         var height = this.size.height - this.getHeaderFooterHeight(false);
29829         bd.setHeight(height-bd.getMargins("tb"));
29830         var hh = this.header.getHeight();
29831         var h = this.size.height-hh;
29832         cb.setHeight(h);
29833         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29834         bw.setHeight(h-cb.getPadding("tb"));
29835         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29836         bd.setWidth(bw.getWidth(true));
29837         if(this.tabs){
29838             this.tabs.syncHeight();
29839             if(Roo.isIE){
29840                 this.tabs.el.repaint();
29841             }
29842         }
29843     },
29844
29845     /**
29846      * Restores the previous state of the dialog if Roo.state is configured.
29847      * @return {Roo.BasicDialog} this
29848      */
29849     restoreState : function(){
29850         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29851         if(box && box.width){
29852             this.xy = [box.x, box.y];
29853             this.resizeTo(box.width, box.height);
29854         }
29855         return this;
29856     },
29857
29858     // private
29859     beforeShow : function(){
29860         this.expand();
29861         if(this.fixedcenter){
29862             this.xy = this.el.getCenterXY(true);
29863         }
29864         if(this.modal){
29865             Roo.get(document.body).addClass("x-body-masked");
29866             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29867             this.mask.show();
29868         }
29869         this.constrainXY();
29870     },
29871
29872     // private
29873     animShow : function(){
29874         var b = Roo.get(this.animateTarget).getBox();
29875         this.proxy.setSize(b.width, b.height);
29876         this.proxy.setLocation(b.x, b.y);
29877         this.proxy.show();
29878         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29879                     true, .35, this.showEl.createDelegate(this));
29880     },
29881
29882     /**
29883      * Shows the dialog.
29884      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29885      * @return {Roo.BasicDialog} this
29886      */
29887     show : function(animateTarget){
29888         if (this.fireEvent("beforeshow", this) === false){
29889             return;
29890         }
29891         if(this.syncHeightBeforeShow){
29892             this.syncBodyHeight();
29893         }else if(this.firstShow){
29894             this.firstShow = false;
29895             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29896         }
29897         this.animateTarget = animateTarget || this.animateTarget;
29898         if(!this.el.isVisible()){
29899             this.beforeShow();
29900             if(this.animateTarget && Roo.get(this.animateTarget)){
29901                 this.animShow();
29902             }else{
29903                 this.showEl();
29904             }
29905         }
29906         return this;
29907     },
29908
29909     // private
29910     showEl : function(){
29911         this.proxy.hide();
29912         this.el.setXY(this.xy);
29913         this.el.show();
29914         this.adjustAssets(true);
29915         this.toFront();
29916         this.focus();
29917         // IE peekaboo bug - fix found by Dave Fenwick
29918         if(Roo.isIE){
29919             this.el.repaint();
29920         }
29921         this.fireEvent("show", this);
29922     },
29923
29924     /**
29925      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29926      * dialog itself will receive focus.
29927      */
29928     focus : function(){
29929         if(this.defaultButton){
29930             this.defaultButton.focus();
29931         }else{
29932             this.focusEl.focus();
29933         }
29934     },
29935
29936     // private
29937     constrainXY : function(){
29938         if(this.constraintoviewport !== false){
29939             if(!this.viewSize){
29940                 if(this.container){
29941                     var s = this.container.getSize();
29942                     this.viewSize = [s.width, s.height];
29943                 }else{
29944                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29945                 }
29946             }
29947             var s = Roo.get(this.container||document).getScroll();
29948
29949             var x = this.xy[0], y = this.xy[1];
29950             var w = this.size.width, h = this.size.height;
29951             var vw = this.viewSize[0], vh = this.viewSize[1];
29952             // only move it if it needs it
29953             var moved = false;
29954             // first validate right/bottom
29955             if(x + w > vw+s.left){
29956                 x = vw - w;
29957                 moved = true;
29958             }
29959             if(y + h > vh+s.top){
29960                 y = vh - h;
29961                 moved = true;
29962             }
29963             // then make sure top/left isn't negative
29964             if(x < s.left){
29965                 x = s.left;
29966                 moved = true;
29967             }
29968             if(y < s.top){
29969                 y = s.top;
29970                 moved = true;
29971             }
29972             if(moved){
29973                 // cache xy
29974                 this.xy = [x, y];
29975                 if(this.isVisible()){
29976                     this.el.setLocation(x, y);
29977                     this.adjustAssets();
29978                 }
29979             }
29980         }
29981     },
29982
29983     // private
29984     onDrag : function(){
29985         if(!this.proxyDrag){
29986             this.xy = this.el.getXY();
29987             this.adjustAssets();
29988         }
29989     },
29990
29991     // private
29992     adjustAssets : function(doShow){
29993         var x = this.xy[0], y = this.xy[1];
29994         var w = this.size.width, h = this.size.height;
29995         if(doShow === true){
29996             if(this.shadow){
29997                 this.shadow.show(this.el);
29998             }
29999             if(this.shim){
30000                 this.shim.show();
30001             }
30002         }
30003         if(this.shadow && this.shadow.isVisible()){
30004             this.shadow.show(this.el);
30005         }
30006         if(this.shim && this.shim.isVisible()){
30007             this.shim.setBounds(x, y, w, h);
30008         }
30009     },
30010
30011     // private
30012     adjustViewport : function(w, h){
30013         if(!w || !h){
30014             w = Roo.lib.Dom.getViewWidth();
30015             h = Roo.lib.Dom.getViewHeight();
30016         }
30017         // cache the size
30018         this.viewSize = [w, h];
30019         if(this.modal && this.mask.isVisible()){
30020             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30021             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30022         }
30023         if(this.isVisible()){
30024             this.constrainXY();
30025         }
30026     },
30027
30028     /**
30029      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30030      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30031      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30032      */
30033     destroy : function(removeEl){
30034         if(this.isVisible()){
30035             this.animateTarget = null;
30036             this.hide();
30037         }
30038         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30039         if(this.tabs){
30040             this.tabs.destroy(removeEl);
30041         }
30042         Roo.destroy(
30043              this.shim,
30044              this.proxy,
30045              this.resizer,
30046              this.close,
30047              this.mask
30048         );
30049         if(this.dd){
30050             this.dd.unreg();
30051         }
30052         if(this.buttons){
30053            for(var i = 0, len = this.buttons.length; i < len; i++){
30054                this.buttons[i].destroy();
30055            }
30056         }
30057         this.el.removeAllListeners();
30058         if(removeEl === true){
30059             this.el.update("");
30060             this.el.remove();
30061         }
30062         Roo.DialogManager.unregister(this);
30063     },
30064
30065     // private
30066     startMove : function(){
30067         if(this.proxyDrag){
30068             this.proxy.show();
30069         }
30070         if(this.constraintoviewport !== false){
30071             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30072         }
30073     },
30074
30075     // private
30076     endMove : function(){
30077         if(!this.proxyDrag){
30078             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30079         }else{
30080             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30081             this.proxy.hide();
30082         }
30083         this.refreshSize();
30084         this.adjustAssets();
30085         this.focus();
30086         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30087     },
30088
30089     /**
30090      * Brings this dialog to the front of any other visible dialogs
30091      * @return {Roo.BasicDialog} this
30092      */
30093     toFront : function(){
30094         Roo.DialogManager.bringToFront(this);
30095         return this;
30096     },
30097
30098     /**
30099      * Sends this dialog to the back (under) of any other visible dialogs
30100      * @return {Roo.BasicDialog} this
30101      */
30102     toBack : function(){
30103         Roo.DialogManager.sendToBack(this);
30104         return this;
30105     },
30106
30107     /**
30108      * Centers this dialog in the viewport
30109      * @return {Roo.BasicDialog} this
30110      */
30111     center : function(){
30112         var xy = this.el.getCenterXY(true);
30113         this.moveTo(xy[0], xy[1]);
30114         return this;
30115     },
30116
30117     /**
30118      * Moves the dialog's top-left corner to the specified point
30119      * @param {Number} x
30120      * @param {Number} y
30121      * @return {Roo.BasicDialog} this
30122      */
30123     moveTo : function(x, y){
30124         this.xy = [x,y];
30125         if(this.isVisible()){
30126             this.el.setXY(this.xy);
30127             this.adjustAssets();
30128         }
30129         return this;
30130     },
30131
30132     /**
30133      * Aligns the dialog to the specified element
30134      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30135      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30136      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30137      * @return {Roo.BasicDialog} this
30138      */
30139     alignTo : function(element, position, offsets){
30140         this.xy = this.el.getAlignToXY(element, position, offsets);
30141         if(this.isVisible()){
30142             this.el.setXY(this.xy);
30143             this.adjustAssets();
30144         }
30145         return this;
30146     },
30147
30148     /**
30149      * Anchors an element to another element and realigns it when the window is resized.
30150      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30151      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30152      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30153      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30154      * is a number, it is used as the buffer delay (defaults to 50ms).
30155      * @return {Roo.BasicDialog} this
30156      */
30157     anchorTo : function(el, alignment, offsets, monitorScroll){
30158         var action = function(){
30159             this.alignTo(el, alignment, offsets);
30160         };
30161         Roo.EventManager.onWindowResize(action, this);
30162         var tm = typeof monitorScroll;
30163         if(tm != 'undefined'){
30164             Roo.EventManager.on(window, 'scroll', action, this,
30165                 {buffer: tm == 'number' ? monitorScroll : 50});
30166         }
30167         action.call(this);
30168         return this;
30169     },
30170
30171     /**
30172      * Returns true if the dialog is visible
30173      * @return {Boolean}
30174      */
30175     isVisible : function(){
30176         return this.el.isVisible();
30177     },
30178
30179     // private
30180     animHide : function(callback){
30181         var b = Roo.get(this.animateTarget).getBox();
30182         this.proxy.show();
30183         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30184         this.el.hide();
30185         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30186                     this.hideEl.createDelegate(this, [callback]));
30187     },
30188
30189     /**
30190      * Hides the dialog.
30191      * @param {Function} callback (optional) Function to call when the dialog is hidden
30192      * @return {Roo.BasicDialog} this
30193      */
30194     hide : function(callback){
30195         if (this.fireEvent("beforehide", this) === false){
30196             return;
30197         }
30198         if(this.shadow){
30199             this.shadow.hide();
30200         }
30201         if(this.shim) {
30202           this.shim.hide();
30203         }
30204         // sometimes animateTarget seems to get set.. causing problems...
30205         // this just double checks..
30206         if(this.animateTarget && Roo.get(this.animateTarget)) {
30207            this.animHide(callback);
30208         }else{
30209             this.el.hide();
30210             this.hideEl(callback);
30211         }
30212         return this;
30213     },
30214
30215     // private
30216     hideEl : function(callback){
30217         this.proxy.hide();
30218         if(this.modal){
30219             this.mask.hide();
30220             Roo.get(document.body).removeClass("x-body-masked");
30221         }
30222         this.fireEvent("hide", this);
30223         if(typeof callback == "function"){
30224             callback();
30225         }
30226     },
30227
30228     // private
30229     hideAction : function(){
30230         this.setLeft("-10000px");
30231         this.setTop("-10000px");
30232         this.setStyle("visibility", "hidden");
30233     },
30234
30235     // private
30236     refreshSize : function(){
30237         this.size = this.el.getSize();
30238         this.xy = this.el.getXY();
30239         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30240     },
30241
30242     // private
30243     // z-index is managed by the DialogManager and may be overwritten at any time
30244     setZIndex : function(index){
30245         if(this.modal){
30246             this.mask.setStyle("z-index", index);
30247         }
30248         if(this.shim){
30249             this.shim.setStyle("z-index", ++index);
30250         }
30251         if(this.shadow){
30252             this.shadow.setZIndex(++index);
30253         }
30254         this.el.setStyle("z-index", ++index);
30255         if(this.proxy){
30256             this.proxy.setStyle("z-index", ++index);
30257         }
30258         if(this.resizer){
30259             this.resizer.proxy.setStyle("z-index", ++index);
30260         }
30261
30262         this.lastZIndex = index;
30263     },
30264
30265     /**
30266      * Returns the element for this dialog
30267      * @return {Roo.Element} The underlying dialog Element
30268      */
30269     getEl : function(){
30270         return this.el;
30271     }
30272 });
30273
30274 /**
30275  * @class Roo.DialogManager
30276  * Provides global access to BasicDialogs that have been created and
30277  * support for z-indexing (layering) multiple open dialogs.
30278  */
30279 Roo.DialogManager = function(){
30280     var list = {};
30281     var accessList = [];
30282     var front = null;
30283
30284     // private
30285     var sortDialogs = function(d1, d2){
30286         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30287     };
30288
30289     // private
30290     var orderDialogs = function(){
30291         accessList.sort(sortDialogs);
30292         var seed = Roo.DialogManager.zseed;
30293         for(var i = 0, len = accessList.length; i < len; i++){
30294             var dlg = accessList[i];
30295             if(dlg){
30296                 dlg.setZIndex(seed + (i*10));
30297             }
30298         }
30299     };
30300
30301     return {
30302         /**
30303          * The starting z-index for BasicDialogs (defaults to 9000)
30304          * @type Number The z-index value
30305          */
30306         zseed : 9000,
30307
30308         // private
30309         register : function(dlg){
30310             list[dlg.id] = dlg;
30311             accessList.push(dlg);
30312         },
30313
30314         // private
30315         unregister : function(dlg){
30316             delete list[dlg.id];
30317             var i=0;
30318             var len=0;
30319             if(!accessList.indexOf){
30320                 for(  i = 0, len = accessList.length; i < len; i++){
30321                     if(accessList[i] == dlg){
30322                         accessList.splice(i, 1);
30323                         return;
30324                     }
30325                 }
30326             }else{
30327                  i = accessList.indexOf(dlg);
30328                 if(i != -1){
30329                     accessList.splice(i, 1);
30330                 }
30331             }
30332         },
30333
30334         /**
30335          * Gets a registered dialog by id
30336          * @param {String/Object} id The id of the dialog or a dialog
30337          * @return {Roo.BasicDialog} this
30338          */
30339         get : function(id){
30340             return typeof id == "object" ? id : list[id];
30341         },
30342
30343         /**
30344          * Brings the specified dialog to the front
30345          * @param {String/Object} dlg The id of the dialog or a dialog
30346          * @return {Roo.BasicDialog} this
30347          */
30348         bringToFront : function(dlg){
30349             dlg = this.get(dlg);
30350             if(dlg != front){
30351                 front = dlg;
30352                 dlg._lastAccess = new Date().getTime();
30353                 orderDialogs();
30354             }
30355             return dlg;
30356         },
30357
30358         /**
30359          * Sends the specified dialog to the back
30360          * @param {String/Object} dlg The id of the dialog or a dialog
30361          * @return {Roo.BasicDialog} this
30362          */
30363         sendToBack : function(dlg){
30364             dlg = this.get(dlg);
30365             dlg._lastAccess = -(new Date().getTime());
30366             orderDialogs();
30367             return dlg;
30368         },
30369
30370         /**
30371          * Hides all dialogs
30372          */
30373         hideAll : function(){
30374             for(var id in list){
30375                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
30376                     list[id].hide();
30377                 }
30378             }
30379         }
30380     };
30381 }();
30382
30383 /**
30384  * @class Roo.LayoutDialog
30385  * @extends Roo.BasicDialog
30386  * Dialog which provides adjustments for working with a layout in a Dialog.
30387  * Add your necessary layout config options to the dialog's config.<br>
30388  * Example usage (including a nested layout):
30389  * <pre><code>
30390 if(!dialog){
30391     dialog = new Roo.LayoutDialog("download-dlg", {
30392         modal: true,
30393         width:600,
30394         height:450,
30395         shadow:true,
30396         minWidth:500,
30397         minHeight:350,
30398         autoTabs:true,
30399         proxyDrag:true,
30400         // layout config merges with the dialog config
30401         center:{
30402             tabPosition: "top",
30403             alwaysShowTabs: true
30404         }
30405     });
30406     dialog.addKeyListener(27, dialog.hide, dialog);
30407     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
30408     dialog.addButton("Build It!", this.getDownload, this);
30409
30410     // we can even add nested layouts
30411     var innerLayout = new Roo.BorderLayout("dl-inner", {
30412         east: {
30413             initialSize: 200,
30414             autoScroll:true,
30415             split:true
30416         },
30417         center: {
30418             autoScroll:true
30419         }
30420     });
30421     innerLayout.beginUpdate();
30422     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
30423     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
30424     innerLayout.endUpdate(true);
30425
30426     var layout = dialog.getLayout();
30427     layout.beginUpdate();
30428     layout.add("center", new Roo.ContentPanel("standard-panel",
30429                         {title: "Download the Source", fitToFrame:true}));
30430     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
30431                {title: "Build your own roo.js"}));
30432     layout.getRegion("center").showPanel(sp);
30433     layout.endUpdate();
30434 }
30435 </code></pre>
30436     * @constructor
30437     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
30438     * @param {Object} config configuration options
30439   */
30440 Roo.LayoutDialog = function(el, cfg){
30441     
30442     var config=  cfg;
30443     if (typeof(cfg) == 'undefined') {
30444         config = Roo.apply({}, el);
30445         // not sure why we use documentElement here.. - it should always be body.
30446         // IE7 borks horribly if we use documentElement.
30447         // webkit also does not like documentElement - it creates a body element...
30448         el = Roo.get( document.body || document.documentElement ).createChild();
30449         //config.autoCreate = true;
30450     }
30451     
30452     
30453     config.autoTabs = false;
30454     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
30455     this.body.setStyle({overflow:"hidden", position:"relative"});
30456     this.layout = new Roo.BorderLayout(this.body.dom, config);
30457     this.layout.monitorWindowResize = false;
30458     this.el.addClass("x-dlg-auto-layout");
30459     // fix case when center region overwrites center function
30460     this.center = Roo.BasicDialog.prototype.center;
30461     this.on("show", this.layout.layout, this.layout, true);
30462     if (config.items) {
30463         var xitems = config.items;
30464         delete config.items;
30465         Roo.each(xitems, this.addxtype, this);
30466     }
30467     
30468     
30469 };
30470 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
30471     /**
30472      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
30473      * @deprecated
30474      */
30475     endUpdate : function(){
30476         this.layout.endUpdate();
30477     },
30478
30479     /**
30480      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
30481      *  @deprecated
30482      */
30483     beginUpdate : function(){
30484         this.layout.beginUpdate();
30485     },
30486
30487     /**
30488      * Get the BorderLayout for this dialog
30489      * @return {Roo.BorderLayout}
30490      */
30491     getLayout : function(){
30492         return this.layout;
30493     },
30494
30495     showEl : function(){
30496         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
30497         if(Roo.isIE7){
30498             this.layout.layout();
30499         }
30500     },
30501
30502     // private
30503     // Use the syncHeightBeforeShow config option to control this automatically
30504     syncBodyHeight : function(){
30505         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
30506         if(this.layout){this.layout.layout();}
30507     },
30508     
30509       /**
30510      * Add an xtype element (actually adds to the layout.)
30511      * @return {Object} xdata xtype object data.
30512      */
30513     
30514     addxtype : function(c) {
30515         return this.layout.addxtype(c);
30516     }
30517 });/*
30518  * Based on:
30519  * Ext JS Library 1.1.1
30520  * Copyright(c) 2006-2007, Ext JS, LLC.
30521  *
30522  * Originally Released Under LGPL - original licence link has changed is not relivant.
30523  *
30524  * Fork - LGPL
30525  * <script type="text/javascript">
30526  */
30527  
30528 /**
30529  * @class Roo.MessageBox
30530  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
30531  * Example usage:
30532  *<pre><code>
30533 // Basic alert:
30534 Roo.Msg.alert('Status', 'Changes saved successfully.');
30535
30536 // Prompt for user data:
30537 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
30538     if (btn == 'ok'){
30539         // process text value...
30540     }
30541 });
30542
30543 // Show a dialog using config options:
30544 Roo.Msg.show({
30545    title:'Save Changes?',
30546    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
30547    buttons: Roo.Msg.YESNOCANCEL,
30548    fn: processResult,
30549    animEl: 'elId'
30550 });
30551 </code></pre>
30552  * @singleton
30553  */
30554 Roo.MessageBox = function(){
30555     var dlg, opt, mask, waitTimer;
30556     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30557     var buttons, activeTextEl, bwidth;
30558
30559     // private
30560     var handleButton = function(button){
30561         dlg.hide();
30562         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30563     };
30564
30565     // private
30566     var handleHide = function(){
30567         if(opt && opt.cls){
30568             dlg.el.removeClass(opt.cls);
30569         }
30570         if(waitTimer){
30571             Roo.TaskMgr.stop(waitTimer);
30572             waitTimer = null;
30573         }
30574     };
30575
30576     // private
30577     var updateButtons = function(b){
30578         var width = 0;
30579         if(!b){
30580             buttons["ok"].hide();
30581             buttons["cancel"].hide();
30582             buttons["yes"].hide();
30583             buttons["no"].hide();
30584             dlg.footer.dom.style.display = 'none';
30585             return width;
30586         }
30587         dlg.footer.dom.style.display = '';
30588         for(var k in buttons){
30589             if(typeof buttons[k] != "function"){
30590                 if(b[k]){
30591                     buttons[k].show();
30592                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30593                     width += buttons[k].el.getWidth()+15;
30594                 }else{
30595                     buttons[k].hide();
30596                 }
30597             }
30598         }
30599         return width;
30600     };
30601
30602     // private
30603     var handleEsc = function(d, k, e){
30604         if(opt && opt.closable !== false){
30605             dlg.hide();
30606         }
30607         if(e){
30608             e.stopEvent();
30609         }
30610     };
30611
30612     return {
30613         /**
30614          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30615          * @return {Roo.BasicDialog} The BasicDialog element
30616          */
30617         getDialog : function(){
30618            if(!dlg){
30619                 dlg = new Roo.BasicDialog("x-msg-box", {
30620                     autoCreate : true,
30621                     shadow: true,
30622                     draggable: true,
30623                     resizable:false,
30624                     constraintoviewport:false,
30625                     fixedcenter:true,
30626                     collapsible : false,
30627                     shim:true,
30628                     modal: true,
30629                     width:400, height:100,
30630                     buttonAlign:"center",
30631                     closeClick : function(){
30632                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30633                             handleButton("no");
30634                         }else{
30635                             handleButton("cancel");
30636                         }
30637                     }
30638                 });
30639                 dlg.on("hide", handleHide);
30640                 mask = dlg.mask;
30641                 dlg.addKeyListener(27, handleEsc);
30642                 buttons = {};
30643                 var bt = this.buttonText;
30644                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30645                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30646                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30647                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30648                 bodyEl = dlg.body.createChild({
30649
30650                     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>'
30651                 });
30652                 msgEl = bodyEl.dom.firstChild;
30653                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30654                 textboxEl.enableDisplayMode();
30655                 textboxEl.addKeyListener([10,13], function(){
30656                     if(dlg.isVisible() && opt && opt.buttons){
30657                         if(opt.buttons.ok){
30658                             handleButton("ok");
30659                         }else if(opt.buttons.yes){
30660                             handleButton("yes");
30661                         }
30662                     }
30663                 });
30664                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30665                 textareaEl.enableDisplayMode();
30666                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30667                 progressEl.enableDisplayMode();
30668                 var pf = progressEl.dom.firstChild;
30669                 if (pf) {
30670                     pp = Roo.get(pf.firstChild);
30671                     pp.setHeight(pf.offsetHeight);
30672                 }
30673                 
30674             }
30675             return dlg;
30676         },
30677
30678         /**
30679          * Updates the message box body text
30680          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30681          * the XHTML-compliant non-breaking space character '&amp;#160;')
30682          * @return {Roo.MessageBox} This message box
30683          */
30684         updateText : function(text){
30685             if(!dlg.isVisible() && !opt.width){
30686                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30687             }
30688             msgEl.innerHTML = text || '&#160;';
30689       
30690             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30691             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30692             var w = Math.max(
30693                     Math.min(opt.width || cw , this.maxWidth), 
30694                     Math.max(opt.minWidth || this.minWidth, bwidth)
30695             );
30696             if(opt.prompt){
30697                 activeTextEl.setWidth(w);
30698             }
30699             if(dlg.isVisible()){
30700                 dlg.fixedcenter = false;
30701             }
30702             // to big, make it scroll. = But as usual stupid IE does not support
30703             // !important..
30704             
30705             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30706                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30707                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30708             } else {
30709                 bodyEl.dom.style.height = '';
30710                 bodyEl.dom.style.overflowY = '';
30711             }
30712             if (cw > w) {
30713                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30714             } else {
30715                 bodyEl.dom.style.overflowX = '';
30716             }
30717             
30718             dlg.setContentSize(w, bodyEl.getHeight());
30719             if(dlg.isVisible()){
30720                 dlg.fixedcenter = true;
30721             }
30722             return this;
30723         },
30724
30725         /**
30726          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30727          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30728          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30729          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30730          * @return {Roo.MessageBox} This message box
30731          */
30732         updateProgress : function(value, text){
30733             if(text){
30734                 this.updateText(text);
30735             }
30736             if (pp) { // weird bug on my firefox - for some reason this is not defined
30737                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30738             }
30739             return this;
30740         },        
30741
30742         /**
30743          * Returns true if the message box is currently displayed
30744          * @return {Boolean} True if the message box is visible, else false
30745          */
30746         isVisible : function(){
30747             return dlg && dlg.isVisible();  
30748         },
30749
30750         /**
30751          * Hides the message box if it is displayed
30752          */
30753         hide : function(){
30754             if(this.isVisible()){
30755                 dlg.hide();
30756             }  
30757         },
30758
30759         /**
30760          * Displays a new message box, or reinitializes an existing message box, based on the config options
30761          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30762          * The following config object properties are supported:
30763          * <pre>
30764 Property    Type             Description
30765 ----------  ---------------  ------------------------------------------------------------------------------------
30766 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30767                                    closes (defaults to undefined)
30768 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30769                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30770 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30771                                    progress and wait dialogs will ignore this property and always hide the
30772                                    close button as they can only be closed programmatically.
30773 cls               String           A custom CSS class to apply to the message box element
30774 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30775                                    displayed (defaults to 75)
30776 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30777                                    function will be btn (the name of the button that was clicked, if applicable,
30778                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30779                                    Progress and wait dialogs will ignore this option since they do not respond to
30780                                    user actions and can only be closed programmatically, so any required function
30781                                    should be called by the same code after it closes the dialog.
30782 icon              String           A CSS class that provides a background image to be used as an icon for
30783                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30784 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30785 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30786 modal             Boolean          False to allow user interaction with the page while the message box is
30787                                    displayed (defaults to true)
30788 msg               String           A string that will replace the existing message box body text (defaults
30789                                    to the XHTML-compliant non-breaking space character '&#160;')
30790 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30791 progress          Boolean          True to display a progress bar (defaults to false)
30792 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30793 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30794 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30795 title             String           The title text
30796 value             String           The string value to set into the active textbox element if displayed
30797 wait              Boolean          True to display a progress bar (defaults to false)
30798 width             Number           The width of the dialog in pixels
30799 </pre>
30800          *
30801          * Example usage:
30802          * <pre><code>
30803 Roo.Msg.show({
30804    title: 'Address',
30805    msg: 'Please enter your address:',
30806    width: 300,
30807    buttons: Roo.MessageBox.OKCANCEL,
30808    multiline: true,
30809    fn: saveAddress,
30810    animEl: 'addAddressBtn'
30811 });
30812 </code></pre>
30813          * @param {Object} config Configuration options
30814          * @return {Roo.MessageBox} This message box
30815          */
30816         show : function(options)
30817         {
30818             
30819             // this causes nightmares if you show one dialog after another
30820             // especially on callbacks..
30821              
30822             if(this.isVisible()){
30823                 
30824                 this.hide();
30825                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30826                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30827                 Roo.log("New Dialog Message:" +  options.msg )
30828                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30829                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30830                 
30831             }
30832             var d = this.getDialog();
30833             opt = options;
30834             d.setTitle(opt.title || "&#160;");
30835             d.close.setDisplayed(opt.closable !== false);
30836             activeTextEl = textboxEl;
30837             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30838             if(opt.prompt){
30839                 if(opt.multiline){
30840                     textboxEl.hide();
30841                     textareaEl.show();
30842                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30843                         opt.multiline : this.defaultTextHeight);
30844                     activeTextEl = textareaEl;
30845                 }else{
30846                     textboxEl.show();
30847                     textareaEl.hide();
30848                 }
30849             }else{
30850                 textboxEl.hide();
30851                 textareaEl.hide();
30852             }
30853             progressEl.setDisplayed(opt.progress === true);
30854             this.updateProgress(0);
30855             activeTextEl.dom.value = opt.value || "";
30856             if(opt.prompt){
30857                 dlg.setDefaultButton(activeTextEl);
30858             }else{
30859                 var bs = opt.buttons;
30860                 var db = null;
30861                 if(bs && bs.ok){
30862                     db = buttons["ok"];
30863                 }else if(bs && bs.yes){
30864                     db = buttons["yes"];
30865                 }
30866                 dlg.setDefaultButton(db);
30867             }
30868             bwidth = updateButtons(opt.buttons);
30869             this.updateText(opt.msg);
30870             if(opt.cls){
30871                 d.el.addClass(opt.cls);
30872             }
30873             d.proxyDrag = opt.proxyDrag === true;
30874             d.modal = opt.modal !== false;
30875             d.mask = opt.modal !== false ? mask : false;
30876             if(!d.isVisible()){
30877                 // force it to the end of the z-index stack so it gets a cursor in FF
30878                 document.body.appendChild(dlg.el.dom);
30879                 d.animateTarget = null;
30880                 d.show(options.animEl);
30881             }
30882             return this;
30883         },
30884
30885         /**
30886          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30887          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30888          * and closing the message box when the process is complete.
30889          * @param {String} title The title bar text
30890          * @param {String} msg The message box body text
30891          * @return {Roo.MessageBox} This message box
30892          */
30893         progress : function(title, msg){
30894             this.show({
30895                 title : title,
30896                 msg : msg,
30897                 buttons: false,
30898                 progress:true,
30899                 closable:false,
30900                 minWidth: this.minProgressWidth,
30901                 modal : true
30902             });
30903             return this;
30904         },
30905
30906         /**
30907          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30908          * If a callback function is passed it will be called after the user clicks the button, and the
30909          * id of the button that was clicked will be passed as the only parameter to the callback
30910          * (could also be the top-right close button).
30911          * @param {String} title The title bar text
30912          * @param {String} msg The message box body text
30913          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30914          * @param {Object} scope (optional) The scope of the callback function
30915          * @return {Roo.MessageBox} This message box
30916          */
30917         alert : function(title, msg, fn, scope){
30918             this.show({
30919                 title : title,
30920                 msg : msg,
30921                 buttons: this.OK,
30922                 fn: fn,
30923                 scope : scope,
30924                 modal : true
30925             });
30926             return this;
30927         },
30928
30929         /**
30930          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30931          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30932          * You are responsible for closing the message box when the process is complete.
30933          * @param {String} msg The message box body text
30934          * @param {String} title (optional) The title bar text
30935          * @return {Roo.MessageBox} This message box
30936          */
30937         wait : function(msg, title){
30938             this.show({
30939                 title : title,
30940                 msg : msg,
30941                 buttons: false,
30942                 closable:false,
30943                 progress:true,
30944                 modal:true,
30945                 width:300,
30946                 wait:true
30947             });
30948             waitTimer = Roo.TaskMgr.start({
30949                 run: function(i){
30950                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30951                 },
30952                 interval: 1000
30953             });
30954             return this;
30955         },
30956
30957         /**
30958          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30959          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30960          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30961          * @param {String} title The title bar text
30962          * @param {String} msg The message box body text
30963          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30964          * @param {Object} scope (optional) The scope of the callback function
30965          * @return {Roo.MessageBox} This message box
30966          */
30967         confirm : function(title, msg, fn, scope){
30968             this.show({
30969                 title : title,
30970                 msg : msg,
30971                 buttons: this.YESNO,
30972                 fn: fn,
30973                 scope : scope,
30974                 modal : true
30975             });
30976             return this;
30977         },
30978
30979         /**
30980          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30981          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30982          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30983          * (could also be the top-right close button) and the text that was entered will be passed as the two
30984          * parameters to the callback.
30985          * @param {String} title The title bar text
30986          * @param {String} msg The message box body text
30987          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30988          * @param {Object} scope (optional) The scope of the callback function
30989          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30990          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30991          * @return {Roo.MessageBox} This message box
30992          */
30993         prompt : function(title, msg, fn, scope, multiline){
30994             this.show({
30995                 title : title,
30996                 msg : msg,
30997                 buttons: this.OKCANCEL,
30998                 fn: fn,
30999                 minWidth:250,
31000                 scope : scope,
31001                 prompt:true,
31002                 multiline: multiline,
31003                 modal : true
31004             });
31005             return this;
31006         },
31007
31008         /**
31009          * Button config that displays a single OK button
31010          * @type Object
31011          */
31012         OK : {ok:true},
31013         /**
31014          * Button config that displays Yes and No buttons
31015          * @type Object
31016          */
31017         YESNO : {yes:true, no:true},
31018         /**
31019          * Button config that displays OK and Cancel buttons
31020          * @type Object
31021          */
31022         OKCANCEL : {ok:true, cancel:true},
31023         /**
31024          * Button config that displays Yes, No and Cancel buttons
31025          * @type Object
31026          */
31027         YESNOCANCEL : {yes:true, no:true, cancel:true},
31028
31029         /**
31030          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31031          * @type Number
31032          */
31033         defaultTextHeight : 75,
31034         /**
31035          * The maximum width in pixels of the message box (defaults to 600)
31036          * @type Number
31037          */
31038         maxWidth : 600,
31039         /**
31040          * The minimum width in pixels of the message box (defaults to 100)
31041          * @type Number
31042          */
31043         minWidth : 100,
31044         /**
31045          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31046          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31047          * @type Number
31048          */
31049         minProgressWidth : 250,
31050         /**
31051          * An object containing the default button text strings that can be overriden for localized language support.
31052          * Supported properties are: ok, cancel, yes and no.
31053          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31054          * @type Object
31055          */
31056         buttonText : {
31057             ok : "OK",
31058             cancel : "Cancel",
31059             yes : "Yes",
31060             no : "No"
31061         }
31062     };
31063 }();
31064
31065 /**
31066  * Shorthand for {@link Roo.MessageBox}
31067  */
31068 Roo.Msg = Roo.MessageBox;/*
31069  * Based on:
31070  * Ext JS Library 1.1.1
31071  * Copyright(c) 2006-2007, Ext JS, LLC.
31072  *
31073  * Originally Released Under LGPL - original licence link has changed is not relivant.
31074  *
31075  * Fork - LGPL
31076  * <script type="text/javascript">
31077  */
31078 /**
31079  * @class Roo.QuickTips
31080  * Provides attractive and customizable tooltips for any element.
31081  * @singleton
31082  */
31083 Roo.QuickTips = function(){
31084     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31085     var ce, bd, xy, dd;
31086     var visible = false, disabled = true, inited = false;
31087     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31088     
31089     var onOver = function(e){
31090         if(disabled){
31091             return;
31092         }
31093         var t = e.getTarget();
31094         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31095             return;
31096         }
31097         if(ce && t == ce.el){
31098             clearTimeout(hideProc);
31099             return;
31100         }
31101         if(t && tagEls[t.id]){
31102             tagEls[t.id].el = t;
31103             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31104             return;
31105         }
31106         var ttp, et = Roo.fly(t);
31107         var ns = cfg.namespace;
31108         if(tm.interceptTitles && t.title){
31109             ttp = t.title;
31110             t.qtip = ttp;
31111             t.removeAttribute("title");
31112             e.preventDefault();
31113         }else{
31114             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31115         }
31116         if(ttp){
31117             showProc = show.defer(tm.showDelay, tm, [{
31118                 el: t, 
31119                 text: ttp, 
31120                 width: et.getAttributeNS(ns, cfg.width),
31121                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31122                 title: et.getAttributeNS(ns, cfg.title),
31123                     cls: et.getAttributeNS(ns, cfg.cls)
31124             }]);
31125         }
31126     };
31127     
31128     var onOut = function(e){
31129         clearTimeout(showProc);
31130         var t = e.getTarget();
31131         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31132             hideProc = setTimeout(hide, tm.hideDelay);
31133         }
31134     };
31135     
31136     var onMove = function(e){
31137         if(disabled){
31138             return;
31139         }
31140         xy = e.getXY();
31141         xy[1] += 18;
31142         if(tm.trackMouse && ce){
31143             el.setXY(xy);
31144         }
31145     };
31146     
31147     var onDown = function(e){
31148         clearTimeout(showProc);
31149         clearTimeout(hideProc);
31150         if(!e.within(el)){
31151             if(tm.hideOnClick){
31152                 hide();
31153                 tm.disable();
31154                 tm.enable.defer(100, tm);
31155             }
31156         }
31157     };
31158     
31159     var getPad = function(){
31160         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31161     };
31162
31163     var show = function(o){
31164         if(disabled){
31165             return;
31166         }
31167         clearTimeout(dismissProc);
31168         ce = o;
31169         if(removeCls){ // in case manually hidden
31170             el.removeClass(removeCls);
31171             removeCls = null;
31172         }
31173         if(ce.cls){
31174             el.addClass(ce.cls);
31175             removeCls = ce.cls;
31176         }
31177         if(ce.title){
31178             tipTitle.update(ce.title);
31179             tipTitle.show();
31180         }else{
31181             tipTitle.update('');
31182             tipTitle.hide();
31183         }
31184         el.dom.style.width  = tm.maxWidth+'px';
31185         //tipBody.dom.style.width = '';
31186         tipBodyText.update(o.text);
31187         var p = getPad(), w = ce.width;
31188         if(!w){
31189             var td = tipBodyText.dom;
31190             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31191             if(aw > tm.maxWidth){
31192                 w = tm.maxWidth;
31193             }else if(aw < tm.minWidth){
31194                 w = tm.minWidth;
31195             }else{
31196                 w = aw;
31197             }
31198         }
31199         //tipBody.setWidth(w);
31200         el.setWidth(parseInt(w, 10) + p);
31201         if(ce.autoHide === false){
31202             close.setDisplayed(true);
31203             if(dd){
31204                 dd.unlock();
31205             }
31206         }else{
31207             close.setDisplayed(false);
31208             if(dd){
31209                 dd.lock();
31210             }
31211         }
31212         if(xy){
31213             el.avoidY = xy[1]-18;
31214             el.setXY(xy);
31215         }
31216         if(tm.animate){
31217             el.setOpacity(.1);
31218             el.setStyle("visibility", "visible");
31219             el.fadeIn({callback: afterShow});
31220         }else{
31221             afterShow();
31222         }
31223     };
31224     
31225     var afterShow = function(){
31226         if(ce){
31227             el.show();
31228             esc.enable();
31229             if(tm.autoDismiss && ce.autoHide !== false){
31230                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31231             }
31232         }
31233     };
31234     
31235     var hide = function(noanim){
31236         clearTimeout(dismissProc);
31237         clearTimeout(hideProc);
31238         ce = null;
31239         if(el.isVisible()){
31240             esc.disable();
31241             if(noanim !== true && tm.animate){
31242                 el.fadeOut({callback: afterHide});
31243             }else{
31244                 afterHide();
31245             } 
31246         }
31247     };
31248     
31249     var afterHide = function(){
31250         el.hide();
31251         if(removeCls){
31252             el.removeClass(removeCls);
31253             removeCls = null;
31254         }
31255     };
31256     
31257     return {
31258         /**
31259         * @cfg {Number} minWidth
31260         * The minimum width of the quick tip (defaults to 40)
31261         */
31262        minWidth : 40,
31263         /**
31264         * @cfg {Number} maxWidth
31265         * The maximum width of the quick tip (defaults to 300)
31266         */
31267        maxWidth : 300,
31268         /**
31269         * @cfg {Boolean} interceptTitles
31270         * True to automatically use the element's DOM title value if available (defaults to false)
31271         */
31272        interceptTitles : false,
31273         /**
31274         * @cfg {Boolean} trackMouse
31275         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31276         */
31277        trackMouse : false,
31278         /**
31279         * @cfg {Boolean} hideOnClick
31280         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31281         */
31282        hideOnClick : true,
31283         /**
31284         * @cfg {Number} showDelay
31285         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31286         */
31287        showDelay : 500,
31288         /**
31289         * @cfg {Number} hideDelay
31290         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31291         */
31292        hideDelay : 200,
31293         /**
31294         * @cfg {Boolean} autoHide
31295         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31296         * Used in conjunction with hideDelay.
31297         */
31298        autoHide : true,
31299         /**
31300         * @cfg {Boolean}
31301         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31302         * (defaults to true).  Used in conjunction with autoDismissDelay.
31303         */
31304        autoDismiss : true,
31305         /**
31306         * @cfg {Number}
31307         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31308         */
31309        autoDismissDelay : 5000,
31310        /**
31311         * @cfg {Boolean} animate
31312         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31313         */
31314        animate : false,
31315
31316        /**
31317         * @cfg {String} title
31318         * Title text to display (defaults to '').  This can be any valid HTML markup.
31319         */
31320         title: '',
31321        /**
31322         * @cfg {String} text
31323         * Body text to display (defaults to '').  This can be any valid HTML markup.
31324         */
31325         text : '',
31326        /**
31327         * @cfg {String} cls
31328         * A CSS class to apply to the base quick tip element (defaults to '').
31329         */
31330         cls : '',
31331        /**
31332         * @cfg {Number} width
31333         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
31334         * minWidth or maxWidth.
31335         */
31336         width : null,
31337
31338     /**
31339      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
31340      * or display QuickTips in a page.
31341      */
31342        init : function(){
31343           tm = Roo.QuickTips;
31344           cfg = tm.tagConfig;
31345           if(!inited){
31346               if(!Roo.isReady){ // allow calling of init() before onReady
31347                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
31348                   return;
31349               }
31350               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
31351               el.fxDefaults = {stopFx: true};
31352               // maximum custom styling
31353               //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>');
31354               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>');              
31355               tipTitle = el.child('h3');
31356               tipTitle.enableDisplayMode("block");
31357               tipBody = el.child('div.x-tip-bd');
31358               tipBodyText = el.child('div.x-tip-bd-inner');
31359               //bdLeft = el.child('div.x-tip-bd-left');
31360               //bdRight = el.child('div.x-tip-bd-right');
31361               close = el.child('div.x-tip-close');
31362               close.enableDisplayMode("block");
31363               close.on("click", hide);
31364               var d = Roo.get(document);
31365               d.on("mousedown", onDown);
31366               d.on("mouseover", onOver);
31367               d.on("mouseout", onOut);
31368               d.on("mousemove", onMove);
31369               esc = d.addKeyListener(27, hide);
31370               esc.disable();
31371               if(Roo.dd.DD){
31372                   dd = el.initDD("default", null, {
31373                       onDrag : function(){
31374                           el.sync();  
31375                       }
31376                   });
31377                   dd.setHandleElId(tipTitle.id);
31378                   dd.lock();
31379               }
31380               inited = true;
31381           }
31382           this.enable(); 
31383        },
31384
31385     /**
31386      * Configures a new quick tip instance and assigns it to a target element.  The following config options
31387      * are supported:
31388      * <pre>
31389 Property    Type                   Description
31390 ----------  ---------------------  ------------------------------------------------------------------------
31391 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
31392      * </ul>
31393      * @param {Object} config The config object
31394      */
31395        register : function(config){
31396            var cs = config instanceof Array ? config : arguments;
31397            for(var i = 0, len = cs.length; i < len; i++) {
31398                var c = cs[i];
31399                var target = c.target;
31400                if(target){
31401                    if(target instanceof Array){
31402                        for(var j = 0, jlen = target.length; j < jlen; j++){
31403                            tagEls[target[j]] = c;
31404                        }
31405                    }else{
31406                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
31407                    }
31408                }
31409            }
31410        },
31411
31412     /**
31413      * Removes this quick tip from its element and destroys it.
31414      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
31415      */
31416        unregister : function(el){
31417            delete tagEls[Roo.id(el)];
31418        },
31419
31420     /**
31421      * Enable this quick tip.
31422      */
31423        enable : function(){
31424            if(inited && disabled){
31425                locks.pop();
31426                if(locks.length < 1){
31427                    disabled = false;
31428                }
31429            }
31430        },
31431
31432     /**
31433      * Disable this quick tip.
31434      */
31435        disable : function(){
31436           disabled = true;
31437           clearTimeout(showProc);
31438           clearTimeout(hideProc);
31439           clearTimeout(dismissProc);
31440           if(ce){
31441               hide(true);
31442           }
31443           locks.push(1);
31444        },
31445
31446     /**
31447      * Returns true if the quick tip is enabled, else false.
31448      */
31449        isEnabled : function(){
31450             return !disabled;
31451        },
31452
31453         // private
31454        tagConfig : {
31455            namespace : "ext",
31456            attribute : "qtip",
31457            width : "width",
31458            target : "target",
31459            title : "qtitle",
31460            hide : "hide",
31461            cls : "qclass"
31462        }
31463    };
31464 }();
31465
31466 // backwards compat
31467 Roo.QuickTips.tips = Roo.QuickTips.register;/*
31468  * Based on:
31469  * Ext JS Library 1.1.1
31470  * Copyright(c) 2006-2007, Ext JS, LLC.
31471  *
31472  * Originally Released Under LGPL - original licence link has changed is not relivant.
31473  *
31474  * Fork - LGPL
31475  * <script type="text/javascript">
31476  */
31477  
31478
31479 /**
31480  * @class Roo.tree.TreePanel
31481  * @extends Roo.data.Tree
31482
31483  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
31484  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
31485  * @cfg {Boolean} enableDD true to enable drag and drop
31486  * @cfg {Boolean} enableDrag true to enable just drag
31487  * @cfg {Boolean} enableDrop true to enable just drop
31488  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
31489  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
31490  * @cfg {String} ddGroup The DD group this TreePanel belongs to
31491  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
31492  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
31493  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
31494  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
31495  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
31496  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
31497  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
31498  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
31499  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
31500  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
31501  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
31502  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
31503  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
31504  * 
31505  * @constructor
31506  * @param {String/HTMLElement/Element} el The container element
31507  * @param {Object} config
31508  */
31509 Roo.tree.TreePanel = function(el, config){
31510     var root = false;
31511     var loader = false;
31512     if (config.root) {
31513         root = config.root;
31514         delete config.root;
31515     }
31516     if (config.loader) {
31517         loader = config.loader;
31518         delete config.loader;
31519     }
31520     
31521     Roo.apply(this, config);
31522     Roo.tree.TreePanel.superclass.constructor.call(this);
31523     this.el = Roo.get(el);
31524     this.el.addClass('x-tree');
31525     //console.log(root);
31526     if (root) {
31527         this.setRootNode( Roo.factory(root, Roo.tree));
31528     }
31529     if (loader) {
31530         this.loader = Roo.factory(loader, Roo.tree);
31531     }
31532    /**
31533     * Read-only. The id of the container element becomes this TreePanel's id.
31534     */
31535     this.id = this.el.id;
31536     this.addEvents({
31537         /**
31538         * @event beforeload
31539         * Fires before a node is loaded, return false to cancel
31540         * @param {Node} node The node being loaded
31541         */
31542         "beforeload" : true,
31543         /**
31544         * @event load
31545         * Fires when a node is loaded
31546         * @param {Node} node The node that was loaded
31547         */
31548         "load" : true,
31549         /**
31550         * @event textchange
31551         * Fires when the text for a node is changed
31552         * @param {Node} node The node
31553         * @param {String} text The new text
31554         * @param {String} oldText The old text
31555         */
31556         "textchange" : true,
31557         /**
31558         * @event beforeexpand
31559         * Fires before a node is expanded, return false to cancel.
31560         * @param {Node} node The node
31561         * @param {Boolean} deep
31562         * @param {Boolean} anim
31563         */
31564         "beforeexpand" : true,
31565         /**
31566         * @event beforecollapse
31567         * Fires before a node is collapsed, return false to cancel.
31568         * @param {Node} node The node
31569         * @param {Boolean} deep
31570         * @param {Boolean} anim
31571         */
31572         "beforecollapse" : true,
31573         /**
31574         * @event expand
31575         * Fires when a node is expanded
31576         * @param {Node} node The node
31577         */
31578         "expand" : true,
31579         /**
31580         * @event disabledchange
31581         * Fires when the disabled status of a node changes
31582         * @param {Node} node The node
31583         * @param {Boolean} disabled
31584         */
31585         "disabledchange" : true,
31586         /**
31587         * @event collapse
31588         * Fires when a node is collapsed
31589         * @param {Node} node The node
31590         */
31591         "collapse" : true,
31592         /**
31593         * @event beforeclick
31594         * Fires before click processing on a node. Return false to cancel the default action.
31595         * @param {Node} node The node
31596         * @param {Roo.EventObject} e The event object
31597         */
31598         "beforeclick":true,
31599         /**
31600         * @event checkchange
31601         * Fires when a node with a checkbox's checked property changes
31602         * @param {Node} this This node
31603         * @param {Boolean} checked
31604         */
31605         "checkchange":true,
31606         /**
31607         * @event click
31608         * Fires when a node is clicked
31609         * @param {Node} node The node
31610         * @param {Roo.EventObject} e The event object
31611         */
31612         "click":true,
31613         /**
31614         * @event dblclick
31615         * Fires when a node is double clicked
31616         * @param {Node} node The node
31617         * @param {Roo.EventObject} e The event object
31618         */
31619         "dblclick":true,
31620         /**
31621         * @event contextmenu
31622         * Fires when a node is right clicked
31623         * @param {Node} node The node
31624         * @param {Roo.EventObject} e The event object
31625         */
31626         "contextmenu":true,
31627         /**
31628         * @event beforechildrenrendered
31629         * Fires right before the child nodes for a node are rendered
31630         * @param {Node} node The node
31631         */
31632         "beforechildrenrendered":true,
31633         /**
31634         * @event startdrag
31635         * Fires when a node starts being dragged
31636         * @param {Roo.tree.TreePanel} this
31637         * @param {Roo.tree.TreeNode} node
31638         * @param {event} e The raw browser event
31639         */ 
31640        "startdrag" : true,
31641        /**
31642         * @event enddrag
31643         * Fires when a drag operation is complete
31644         * @param {Roo.tree.TreePanel} this
31645         * @param {Roo.tree.TreeNode} node
31646         * @param {event} e The raw browser event
31647         */
31648        "enddrag" : true,
31649        /**
31650         * @event dragdrop
31651         * Fires when a dragged node is dropped on a valid DD target
31652         * @param {Roo.tree.TreePanel} this
31653         * @param {Roo.tree.TreeNode} node
31654         * @param {DD} dd The dd it was dropped on
31655         * @param {event} e The raw browser event
31656         */
31657        "dragdrop" : true,
31658        /**
31659         * @event beforenodedrop
31660         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31661         * passed to handlers has the following properties:<br />
31662         * <ul style="padding:5px;padding-left:16px;">
31663         * <li>tree - The TreePanel</li>
31664         * <li>target - The node being targeted for the drop</li>
31665         * <li>data - The drag data from the drag source</li>
31666         * <li>point - The point of the drop - append, above or below</li>
31667         * <li>source - The drag source</li>
31668         * <li>rawEvent - Raw mouse event</li>
31669         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31670         * to be inserted by setting them on this object.</li>
31671         * <li>cancel - Set this to true to cancel the drop.</li>
31672         * </ul>
31673         * @param {Object} dropEvent
31674         */
31675        "beforenodedrop" : true,
31676        /**
31677         * @event nodedrop
31678         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31679         * passed to handlers has the following properties:<br />
31680         * <ul style="padding:5px;padding-left:16px;">
31681         * <li>tree - The TreePanel</li>
31682         * <li>target - The node being targeted for the drop</li>
31683         * <li>data - The drag data from the drag source</li>
31684         * <li>point - The point of the drop - append, above or below</li>
31685         * <li>source - The drag source</li>
31686         * <li>rawEvent - Raw mouse event</li>
31687         * <li>dropNode - Dropped node(s).</li>
31688         * </ul>
31689         * @param {Object} dropEvent
31690         */
31691        "nodedrop" : true,
31692         /**
31693         * @event nodedragover
31694         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31695         * passed to handlers has the following properties:<br />
31696         * <ul style="padding:5px;padding-left:16px;">
31697         * <li>tree - The TreePanel</li>
31698         * <li>target - The node being targeted for the drop</li>
31699         * <li>data - The drag data from the drag source</li>
31700         * <li>point - The point of the drop - append, above or below</li>
31701         * <li>source - The drag source</li>
31702         * <li>rawEvent - Raw mouse event</li>
31703         * <li>dropNode - Drop node(s) provided by the source.</li>
31704         * <li>cancel - Set this to true to signal drop not allowed.</li>
31705         * </ul>
31706         * @param {Object} dragOverEvent
31707         */
31708        "nodedragover" : true
31709         
31710     });
31711     if(this.singleExpand){
31712        this.on("beforeexpand", this.restrictExpand, this);
31713     }
31714     if (this.editor) {
31715         this.editor.tree = this;
31716         this.editor = Roo.factory(this.editor, Roo.tree);
31717     }
31718     
31719     if (this.selModel) {
31720         this.selModel = Roo.factory(this.selModel, Roo.tree);
31721     }
31722    
31723 };
31724 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31725     rootVisible : true,
31726     animate: Roo.enableFx,
31727     lines : true,
31728     enableDD : false,
31729     hlDrop : Roo.enableFx,
31730   
31731     renderer: false,
31732     
31733     rendererTip: false,
31734     // private
31735     restrictExpand : function(node){
31736         var p = node.parentNode;
31737         if(p){
31738             if(p.expandedChild && p.expandedChild.parentNode == p){
31739                 p.expandedChild.collapse();
31740             }
31741             p.expandedChild = node;
31742         }
31743     },
31744
31745     // private override
31746     setRootNode : function(node){
31747         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31748         if(!this.rootVisible){
31749             node.ui = new Roo.tree.RootTreeNodeUI(node);
31750         }
31751         return node;
31752     },
31753
31754     /**
31755      * Returns the container element for this TreePanel
31756      */
31757     getEl : function(){
31758         return this.el;
31759     },
31760
31761     /**
31762      * Returns the default TreeLoader for this TreePanel
31763      */
31764     getLoader : function(){
31765         return this.loader;
31766     },
31767
31768     /**
31769      * Expand all nodes
31770      */
31771     expandAll : function(){
31772         this.root.expand(true);
31773     },
31774
31775     /**
31776      * Collapse all nodes
31777      */
31778     collapseAll : function(){
31779         this.root.collapse(true);
31780     },
31781
31782     /**
31783      * Returns the selection model used by this TreePanel
31784      */
31785     getSelectionModel : function(){
31786         if(!this.selModel){
31787             this.selModel = new Roo.tree.DefaultSelectionModel();
31788         }
31789         return this.selModel;
31790     },
31791
31792     /**
31793      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31794      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31795      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31796      * @return {Array}
31797      */
31798     getChecked : function(a, startNode){
31799         startNode = startNode || this.root;
31800         var r = [];
31801         var f = function(){
31802             if(this.attributes.checked){
31803                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31804             }
31805         }
31806         startNode.cascade(f);
31807         return r;
31808     },
31809
31810     /**
31811      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31812      * @param {String} path
31813      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31814      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31815      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31816      */
31817     expandPath : function(path, attr, callback){
31818         attr = attr || "id";
31819         var keys = path.split(this.pathSeparator);
31820         var curNode = this.root;
31821         if(curNode.attributes[attr] != keys[1]){ // invalid root
31822             if(callback){
31823                 callback(false, null);
31824             }
31825             return;
31826         }
31827         var index = 1;
31828         var f = function(){
31829             if(++index == keys.length){
31830                 if(callback){
31831                     callback(true, curNode);
31832                 }
31833                 return;
31834             }
31835             var c = curNode.findChild(attr, keys[index]);
31836             if(!c){
31837                 if(callback){
31838                     callback(false, curNode);
31839                 }
31840                 return;
31841             }
31842             curNode = c;
31843             c.expand(false, false, f);
31844         };
31845         curNode.expand(false, false, f);
31846     },
31847
31848     /**
31849      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31850      * @param {String} path
31851      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31852      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31853      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31854      */
31855     selectPath : function(path, attr, callback){
31856         attr = attr || "id";
31857         var keys = path.split(this.pathSeparator);
31858         var v = keys.pop();
31859         if(keys.length > 0){
31860             var f = function(success, node){
31861                 if(success && node){
31862                     var n = node.findChild(attr, v);
31863                     if(n){
31864                         n.select();
31865                         if(callback){
31866                             callback(true, n);
31867                         }
31868                     }else if(callback){
31869                         callback(false, n);
31870                     }
31871                 }else{
31872                     if(callback){
31873                         callback(false, n);
31874                     }
31875                 }
31876             };
31877             this.expandPath(keys.join(this.pathSeparator), attr, f);
31878         }else{
31879             this.root.select();
31880             if(callback){
31881                 callback(true, this.root);
31882             }
31883         }
31884     },
31885
31886     getTreeEl : function(){
31887         return this.el;
31888     },
31889
31890     /**
31891      * Trigger rendering of this TreePanel
31892      */
31893     render : function(){
31894         if (this.innerCt) {
31895             return this; // stop it rendering more than once!!
31896         }
31897         
31898         this.innerCt = this.el.createChild({tag:"ul",
31899                cls:"x-tree-root-ct " +
31900                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31901
31902         if(this.containerScroll){
31903             Roo.dd.ScrollManager.register(this.el);
31904         }
31905         if((this.enableDD || this.enableDrop) && !this.dropZone){
31906            /**
31907             * The dropZone used by this tree if drop is enabled
31908             * @type Roo.tree.TreeDropZone
31909             */
31910              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31911                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31912            });
31913         }
31914         if((this.enableDD || this.enableDrag) && !this.dragZone){
31915            /**
31916             * The dragZone used by this tree if drag is enabled
31917             * @type Roo.tree.TreeDragZone
31918             */
31919             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31920                ddGroup: this.ddGroup || "TreeDD",
31921                scroll: this.ddScroll
31922            });
31923         }
31924         this.getSelectionModel().init(this);
31925         if (!this.root) {
31926             Roo.log("ROOT not set in tree");
31927             return this;
31928         }
31929         this.root.render();
31930         if(!this.rootVisible){
31931             this.root.renderChildren();
31932         }
31933         return this;
31934     }
31935 });/*
31936  * Based on:
31937  * Ext JS Library 1.1.1
31938  * Copyright(c) 2006-2007, Ext JS, LLC.
31939  *
31940  * Originally Released Under LGPL - original licence link has changed is not relivant.
31941  *
31942  * Fork - LGPL
31943  * <script type="text/javascript">
31944  */
31945  
31946
31947 /**
31948  * @class Roo.tree.DefaultSelectionModel
31949  * @extends Roo.util.Observable
31950  * The default single selection for a TreePanel.
31951  * @param {Object} cfg Configuration
31952  */
31953 Roo.tree.DefaultSelectionModel = function(cfg){
31954    this.selNode = null;
31955    
31956    
31957    
31958    this.addEvents({
31959        /**
31960         * @event selectionchange
31961         * Fires when the selected node changes
31962         * @param {DefaultSelectionModel} this
31963         * @param {TreeNode} node the new selection
31964         */
31965        "selectionchange" : true,
31966
31967        /**
31968         * @event beforeselect
31969         * Fires before the selected node changes, return false to cancel the change
31970         * @param {DefaultSelectionModel} this
31971         * @param {TreeNode} node the new selection
31972         * @param {TreeNode} node the old selection
31973         */
31974        "beforeselect" : true
31975    });
31976    
31977     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31978 };
31979
31980 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31981     init : function(tree){
31982         this.tree = tree;
31983         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31984         tree.on("click", this.onNodeClick, this);
31985     },
31986     
31987     onNodeClick : function(node, e){
31988         if (e.ctrlKey && this.selNode == node)  {
31989             this.unselect(node);
31990             return;
31991         }
31992         this.select(node);
31993     },
31994     
31995     /**
31996      * Select a node.
31997      * @param {TreeNode} node The node to select
31998      * @return {TreeNode} The selected node
31999      */
32000     select : function(node){
32001         var last = this.selNode;
32002         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32003             if(last){
32004                 last.ui.onSelectedChange(false);
32005             }
32006             this.selNode = node;
32007             node.ui.onSelectedChange(true);
32008             this.fireEvent("selectionchange", this, node, last);
32009         }
32010         return node;
32011     },
32012     
32013     /**
32014      * Deselect a node.
32015      * @param {TreeNode} node The node to unselect
32016      */
32017     unselect : function(node){
32018         if(this.selNode == node){
32019             this.clearSelections();
32020         }    
32021     },
32022     
32023     /**
32024      * Clear all selections
32025      */
32026     clearSelections : function(){
32027         var n = this.selNode;
32028         if(n){
32029             n.ui.onSelectedChange(false);
32030             this.selNode = null;
32031             this.fireEvent("selectionchange", this, null);
32032         }
32033         return n;
32034     },
32035     
32036     /**
32037      * Get the selected node
32038      * @return {TreeNode} The selected node
32039      */
32040     getSelectedNode : function(){
32041         return this.selNode;    
32042     },
32043     
32044     /**
32045      * Returns true if the node is selected
32046      * @param {TreeNode} node The node to check
32047      * @return {Boolean}
32048      */
32049     isSelected : function(node){
32050         return this.selNode == node;  
32051     },
32052
32053     /**
32054      * Selects the node above the selected node in the tree, intelligently walking the nodes
32055      * @return TreeNode The new selection
32056      */
32057     selectPrevious : function(){
32058         var s = this.selNode || this.lastSelNode;
32059         if(!s){
32060             return null;
32061         }
32062         var ps = s.previousSibling;
32063         if(ps){
32064             if(!ps.isExpanded() || ps.childNodes.length < 1){
32065                 return this.select(ps);
32066             } else{
32067                 var lc = ps.lastChild;
32068                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32069                     lc = lc.lastChild;
32070                 }
32071                 return this.select(lc);
32072             }
32073         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32074             return this.select(s.parentNode);
32075         }
32076         return null;
32077     },
32078
32079     /**
32080      * Selects the node above the selected node in the tree, intelligently walking the nodes
32081      * @return TreeNode The new selection
32082      */
32083     selectNext : function(){
32084         var s = this.selNode || this.lastSelNode;
32085         if(!s){
32086             return null;
32087         }
32088         if(s.firstChild && s.isExpanded()){
32089              return this.select(s.firstChild);
32090          }else if(s.nextSibling){
32091              return this.select(s.nextSibling);
32092          }else if(s.parentNode){
32093             var newS = null;
32094             s.parentNode.bubble(function(){
32095                 if(this.nextSibling){
32096                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32097                     return false;
32098                 }
32099             });
32100             return newS;
32101          }
32102         return null;
32103     },
32104
32105     onKeyDown : function(e){
32106         var s = this.selNode || this.lastSelNode;
32107         // undesirable, but required
32108         var sm = this;
32109         if(!s){
32110             return;
32111         }
32112         var k = e.getKey();
32113         switch(k){
32114              case e.DOWN:
32115                  e.stopEvent();
32116                  this.selectNext();
32117              break;
32118              case e.UP:
32119                  e.stopEvent();
32120                  this.selectPrevious();
32121              break;
32122              case e.RIGHT:
32123                  e.preventDefault();
32124                  if(s.hasChildNodes()){
32125                      if(!s.isExpanded()){
32126                          s.expand();
32127                      }else if(s.firstChild){
32128                          this.select(s.firstChild, e);
32129                      }
32130                  }
32131              break;
32132              case e.LEFT:
32133                  e.preventDefault();
32134                  if(s.hasChildNodes() && s.isExpanded()){
32135                      s.collapse();
32136                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32137                      this.select(s.parentNode, e);
32138                  }
32139              break;
32140         };
32141     }
32142 });
32143
32144 /**
32145  * @class Roo.tree.MultiSelectionModel
32146  * @extends Roo.util.Observable
32147  * Multi selection for a TreePanel.
32148  * @param {Object} cfg Configuration
32149  */
32150 Roo.tree.MultiSelectionModel = function(){
32151    this.selNodes = [];
32152    this.selMap = {};
32153    this.addEvents({
32154        /**
32155         * @event selectionchange
32156         * Fires when the selected nodes change
32157         * @param {MultiSelectionModel} this
32158         * @param {Array} nodes Array of the selected nodes
32159         */
32160        "selectionchange" : true
32161    });
32162    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32163    
32164 };
32165
32166 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32167     init : function(tree){
32168         this.tree = tree;
32169         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32170         tree.on("click", this.onNodeClick, this);
32171     },
32172     
32173     onNodeClick : function(node, e){
32174         this.select(node, e, e.ctrlKey);
32175     },
32176     
32177     /**
32178      * Select a node.
32179      * @param {TreeNode} node The node to select
32180      * @param {EventObject} e (optional) An event associated with the selection
32181      * @param {Boolean} keepExisting True to retain existing selections
32182      * @return {TreeNode} The selected node
32183      */
32184     select : function(node, e, keepExisting){
32185         if(keepExisting !== true){
32186             this.clearSelections(true);
32187         }
32188         if(this.isSelected(node)){
32189             this.lastSelNode = node;
32190             return node;
32191         }
32192         this.selNodes.push(node);
32193         this.selMap[node.id] = node;
32194         this.lastSelNode = node;
32195         node.ui.onSelectedChange(true);
32196         this.fireEvent("selectionchange", this, this.selNodes);
32197         return node;
32198     },
32199     
32200     /**
32201      * Deselect a node.
32202      * @param {TreeNode} node The node to unselect
32203      */
32204     unselect : function(node){
32205         if(this.selMap[node.id]){
32206             node.ui.onSelectedChange(false);
32207             var sn = this.selNodes;
32208             var index = -1;
32209             if(sn.indexOf){
32210                 index = sn.indexOf(node);
32211             }else{
32212                 for(var i = 0, len = sn.length; i < len; i++){
32213                     if(sn[i] == node){
32214                         index = i;
32215                         break;
32216                     }
32217                 }
32218             }
32219             if(index != -1){
32220                 this.selNodes.splice(index, 1);
32221             }
32222             delete this.selMap[node.id];
32223             this.fireEvent("selectionchange", this, this.selNodes);
32224         }
32225     },
32226     
32227     /**
32228      * Clear all selections
32229      */
32230     clearSelections : function(suppressEvent){
32231         var sn = this.selNodes;
32232         if(sn.length > 0){
32233             for(var i = 0, len = sn.length; i < len; i++){
32234                 sn[i].ui.onSelectedChange(false);
32235             }
32236             this.selNodes = [];
32237             this.selMap = {};
32238             if(suppressEvent !== true){
32239                 this.fireEvent("selectionchange", this, this.selNodes);
32240             }
32241         }
32242     },
32243     
32244     /**
32245      * Returns true if the node is selected
32246      * @param {TreeNode} node The node to check
32247      * @return {Boolean}
32248      */
32249     isSelected : function(node){
32250         return this.selMap[node.id] ? true : false;  
32251     },
32252     
32253     /**
32254      * Returns an array of the selected nodes
32255      * @return {Array}
32256      */
32257     getSelectedNodes : function(){
32258         return this.selNodes;    
32259     },
32260
32261     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32262
32263     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32264
32265     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32266 });/*
32267  * Based on:
32268  * Ext JS Library 1.1.1
32269  * Copyright(c) 2006-2007, Ext JS, LLC.
32270  *
32271  * Originally Released Under LGPL - original licence link has changed is not relivant.
32272  *
32273  * Fork - LGPL
32274  * <script type="text/javascript">
32275  */
32276  
32277 /**
32278  * @class Roo.tree.TreeNode
32279  * @extends Roo.data.Node
32280  * @cfg {String} text The text for this node
32281  * @cfg {Boolean} expanded true to start the node expanded
32282  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32283  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32284  * @cfg {Boolean} disabled true to start the node disabled
32285  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32286  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32287  * @cfg {String} cls A css class to be added to the node
32288  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32289  * @cfg {String} href URL of the link used for the node (defaults to #)
32290  * @cfg {String} hrefTarget target frame for the link
32291  * @cfg {String} qtip An Ext QuickTip for the node
32292  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32293  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32294  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32295  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32296  * (defaults to undefined with no checkbox rendered)
32297  * @constructor
32298  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32299  */
32300 Roo.tree.TreeNode = function(attributes){
32301     attributes = attributes || {};
32302     if(typeof attributes == "string"){
32303         attributes = {text: attributes};
32304     }
32305     this.childrenRendered = false;
32306     this.rendered = false;
32307     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32308     this.expanded = attributes.expanded === true;
32309     this.isTarget = attributes.isTarget !== false;
32310     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32311     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32312
32313     /**
32314      * Read-only. The text for this node. To change it use setText().
32315      * @type String
32316      */
32317     this.text = attributes.text;
32318     /**
32319      * True if this node is disabled.
32320      * @type Boolean
32321      */
32322     this.disabled = attributes.disabled === true;
32323
32324     this.addEvents({
32325         /**
32326         * @event textchange
32327         * Fires when the text for this node is changed
32328         * @param {Node} this This node
32329         * @param {String} text The new text
32330         * @param {String} oldText The old text
32331         */
32332         "textchange" : true,
32333         /**
32334         * @event beforeexpand
32335         * Fires before this node is expanded, return false to cancel.
32336         * @param {Node} this This node
32337         * @param {Boolean} deep
32338         * @param {Boolean} anim
32339         */
32340         "beforeexpand" : true,
32341         /**
32342         * @event beforecollapse
32343         * Fires before this node is collapsed, return false to cancel.
32344         * @param {Node} this This node
32345         * @param {Boolean} deep
32346         * @param {Boolean} anim
32347         */
32348         "beforecollapse" : true,
32349         /**
32350         * @event expand
32351         * Fires when this node is expanded
32352         * @param {Node} this This node
32353         */
32354         "expand" : true,
32355         /**
32356         * @event disabledchange
32357         * Fires when the disabled status of this node changes
32358         * @param {Node} this This node
32359         * @param {Boolean} disabled
32360         */
32361         "disabledchange" : true,
32362         /**
32363         * @event collapse
32364         * Fires when this node is collapsed
32365         * @param {Node} this This node
32366         */
32367         "collapse" : true,
32368         /**
32369         * @event beforeclick
32370         * Fires before click processing. Return false to cancel the default action.
32371         * @param {Node} this This node
32372         * @param {Roo.EventObject} e The event object
32373         */
32374         "beforeclick":true,
32375         /**
32376         * @event checkchange
32377         * Fires when a node with a checkbox's checked property changes
32378         * @param {Node} this This node
32379         * @param {Boolean} checked
32380         */
32381         "checkchange":true,
32382         /**
32383         * @event click
32384         * Fires when this node is clicked
32385         * @param {Node} this This node
32386         * @param {Roo.EventObject} e The event object
32387         */
32388         "click":true,
32389         /**
32390         * @event dblclick
32391         * Fires when this node is double clicked
32392         * @param {Node} this This node
32393         * @param {Roo.EventObject} e The event object
32394         */
32395         "dblclick":true,
32396         /**
32397         * @event contextmenu
32398         * Fires when this node is right clicked
32399         * @param {Node} this This node
32400         * @param {Roo.EventObject} e The event object
32401         */
32402         "contextmenu":true,
32403         /**
32404         * @event beforechildrenrendered
32405         * Fires right before the child nodes for this node are rendered
32406         * @param {Node} this This node
32407         */
32408         "beforechildrenrendered":true
32409     });
32410
32411     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
32412
32413     /**
32414      * Read-only. The UI for this node
32415      * @type TreeNodeUI
32416      */
32417     this.ui = new uiClass(this);
32418     
32419     // finally support items[]
32420     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
32421         return;
32422     }
32423     
32424     
32425     Roo.each(this.attributes.items, function(c) {
32426         this.appendChild(Roo.factory(c,Roo.Tree));
32427     }, this);
32428     delete this.attributes.items;
32429     
32430     
32431     
32432 };
32433 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
32434     preventHScroll: true,
32435     /**
32436      * Returns true if this node is expanded
32437      * @return {Boolean}
32438      */
32439     isExpanded : function(){
32440         return this.expanded;
32441     },
32442
32443     /**
32444      * Returns the UI object for this node
32445      * @return {TreeNodeUI}
32446      */
32447     getUI : function(){
32448         return this.ui;
32449     },
32450
32451     // private override
32452     setFirstChild : function(node){
32453         var of = this.firstChild;
32454         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
32455         if(this.childrenRendered && of && node != of){
32456             of.renderIndent(true, true);
32457         }
32458         if(this.rendered){
32459             this.renderIndent(true, true);
32460         }
32461     },
32462
32463     // private override
32464     setLastChild : function(node){
32465         var ol = this.lastChild;
32466         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
32467         if(this.childrenRendered && ol && node != ol){
32468             ol.renderIndent(true, true);
32469         }
32470         if(this.rendered){
32471             this.renderIndent(true, true);
32472         }
32473     },
32474
32475     // these methods are overridden to provide lazy rendering support
32476     // private override
32477     appendChild : function()
32478     {
32479         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
32480         if(node && this.childrenRendered){
32481             node.render();
32482         }
32483         this.ui.updateExpandIcon();
32484         return node;
32485     },
32486
32487     // private override
32488     removeChild : function(node){
32489         this.ownerTree.getSelectionModel().unselect(node);
32490         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
32491         // if it's been rendered remove dom node
32492         if(this.childrenRendered){
32493             node.ui.remove();
32494         }
32495         if(this.childNodes.length < 1){
32496             this.collapse(false, false);
32497         }else{
32498             this.ui.updateExpandIcon();
32499         }
32500         if(!this.firstChild) {
32501             this.childrenRendered = false;
32502         }
32503         return node;
32504     },
32505
32506     // private override
32507     insertBefore : function(node, refNode){
32508         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
32509         if(newNode && refNode && this.childrenRendered){
32510             node.render();
32511         }
32512         this.ui.updateExpandIcon();
32513         return newNode;
32514     },
32515
32516     /**
32517      * Sets the text for this node
32518      * @param {String} text
32519      */
32520     setText : function(text){
32521         var oldText = this.text;
32522         this.text = text;
32523         this.attributes.text = text;
32524         if(this.rendered){ // event without subscribing
32525             this.ui.onTextChange(this, text, oldText);
32526         }
32527         this.fireEvent("textchange", this, text, oldText);
32528     },
32529
32530     /**
32531      * Triggers selection of this node
32532      */
32533     select : function(){
32534         this.getOwnerTree().getSelectionModel().select(this);
32535     },
32536
32537     /**
32538      * Triggers deselection of this node
32539      */
32540     unselect : function(){
32541         this.getOwnerTree().getSelectionModel().unselect(this);
32542     },
32543
32544     /**
32545      * Returns true if this node is selected
32546      * @return {Boolean}
32547      */
32548     isSelected : function(){
32549         return this.getOwnerTree().getSelectionModel().isSelected(this);
32550     },
32551
32552     /**
32553      * Expand this node.
32554      * @param {Boolean} deep (optional) True to expand all children as well
32555      * @param {Boolean} anim (optional) false to cancel the default animation
32556      * @param {Function} callback (optional) A callback to be called when
32557      * expanding this node completes (does not wait for deep expand to complete).
32558      * Called with 1 parameter, this node.
32559      */
32560     expand : function(deep, anim, callback){
32561         if(!this.expanded){
32562             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
32563                 return;
32564             }
32565             if(!this.childrenRendered){
32566                 this.renderChildren();
32567             }
32568             this.expanded = true;
32569             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
32570                 this.ui.animExpand(function(){
32571                     this.fireEvent("expand", this);
32572                     if(typeof callback == "function"){
32573                         callback(this);
32574                     }
32575                     if(deep === true){
32576                         this.expandChildNodes(true);
32577                     }
32578                 }.createDelegate(this));
32579                 return;
32580             }else{
32581                 this.ui.expand();
32582                 this.fireEvent("expand", this);
32583                 if(typeof callback == "function"){
32584                     callback(this);
32585                 }
32586             }
32587         }else{
32588            if(typeof callback == "function"){
32589                callback(this);
32590            }
32591         }
32592         if(deep === true){
32593             this.expandChildNodes(true);
32594         }
32595     },
32596
32597     isHiddenRoot : function(){
32598         return this.isRoot && !this.getOwnerTree().rootVisible;
32599     },
32600
32601     /**
32602      * Collapse this node.
32603      * @param {Boolean} deep (optional) True to collapse all children as well
32604      * @param {Boolean} anim (optional) false to cancel the default animation
32605      */
32606     collapse : function(deep, anim){
32607         if(this.expanded && !this.isHiddenRoot()){
32608             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32609                 return;
32610             }
32611             this.expanded = false;
32612             if((this.getOwnerTree().animate && anim !== false) || anim){
32613                 this.ui.animCollapse(function(){
32614                     this.fireEvent("collapse", this);
32615                     if(deep === true){
32616                         this.collapseChildNodes(true);
32617                     }
32618                 }.createDelegate(this));
32619                 return;
32620             }else{
32621                 this.ui.collapse();
32622                 this.fireEvent("collapse", this);
32623             }
32624         }
32625         if(deep === true){
32626             var cs = this.childNodes;
32627             for(var i = 0, len = cs.length; i < len; i++) {
32628                 cs[i].collapse(true, false);
32629             }
32630         }
32631     },
32632
32633     // private
32634     delayedExpand : function(delay){
32635         if(!this.expandProcId){
32636             this.expandProcId = this.expand.defer(delay, this);
32637         }
32638     },
32639
32640     // private
32641     cancelExpand : function(){
32642         if(this.expandProcId){
32643             clearTimeout(this.expandProcId);
32644         }
32645         this.expandProcId = false;
32646     },
32647
32648     /**
32649      * Toggles expanded/collapsed state of the node
32650      */
32651     toggle : function(){
32652         if(this.expanded){
32653             this.collapse();
32654         }else{
32655             this.expand();
32656         }
32657     },
32658
32659     /**
32660      * Ensures all parent nodes are expanded
32661      */
32662     ensureVisible : function(callback){
32663         var tree = this.getOwnerTree();
32664         tree.expandPath(this.parentNode.getPath(), false, function(){
32665             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32666             Roo.callback(callback);
32667         }.createDelegate(this));
32668     },
32669
32670     /**
32671      * Expand all child nodes
32672      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32673      */
32674     expandChildNodes : function(deep){
32675         var cs = this.childNodes;
32676         for(var i = 0, len = cs.length; i < len; i++) {
32677                 cs[i].expand(deep);
32678         }
32679     },
32680
32681     /**
32682      * Collapse all child nodes
32683      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32684      */
32685     collapseChildNodes : function(deep){
32686         var cs = this.childNodes;
32687         for(var i = 0, len = cs.length; i < len; i++) {
32688                 cs[i].collapse(deep);
32689         }
32690     },
32691
32692     /**
32693      * Disables this node
32694      */
32695     disable : function(){
32696         this.disabled = true;
32697         this.unselect();
32698         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32699             this.ui.onDisableChange(this, true);
32700         }
32701         this.fireEvent("disabledchange", this, true);
32702     },
32703
32704     /**
32705      * Enables this node
32706      */
32707     enable : function(){
32708         this.disabled = false;
32709         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32710             this.ui.onDisableChange(this, false);
32711         }
32712         this.fireEvent("disabledchange", this, false);
32713     },
32714
32715     // private
32716     renderChildren : function(suppressEvent){
32717         if(suppressEvent !== false){
32718             this.fireEvent("beforechildrenrendered", this);
32719         }
32720         var cs = this.childNodes;
32721         for(var i = 0, len = cs.length; i < len; i++){
32722             cs[i].render(true);
32723         }
32724         this.childrenRendered = true;
32725     },
32726
32727     // private
32728     sort : function(fn, scope){
32729         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32730         if(this.childrenRendered){
32731             var cs = this.childNodes;
32732             for(var i = 0, len = cs.length; i < len; i++){
32733                 cs[i].render(true);
32734             }
32735         }
32736     },
32737
32738     // private
32739     render : function(bulkRender){
32740         this.ui.render(bulkRender);
32741         if(!this.rendered){
32742             this.rendered = true;
32743             if(this.expanded){
32744                 this.expanded = false;
32745                 this.expand(false, false);
32746             }
32747         }
32748     },
32749
32750     // private
32751     renderIndent : function(deep, refresh){
32752         if(refresh){
32753             this.ui.childIndent = null;
32754         }
32755         this.ui.renderIndent();
32756         if(deep === true && this.childrenRendered){
32757             var cs = this.childNodes;
32758             for(var i = 0, len = cs.length; i < len; i++){
32759                 cs[i].renderIndent(true, refresh);
32760             }
32761         }
32762     }
32763 });/*
32764  * Based on:
32765  * Ext JS Library 1.1.1
32766  * Copyright(c) 2006-2007, Ext JS, LLC.
32767  *
32768  * Originally Released Under LGPL - original licence link has changed is not relivant.
32769  *
32770  * Fork - LGPL
32771  * <script type="text/javascript">
32772  */
32773  
32774 /**
32775  * @class Roo.tree.AsyncTreeNode
32776  * @extends Roo.tree.TreeNode
32777  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32778  * @constructor
32779  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32780  */
32781  Roo.tree.AsyncTreeNode = function(config){
32782     this.loaded = false;
32783     this.loading = false;
32784     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32785     /**
32786     * @event beforeload
32787     * Fires before this node is loaded, return false to cancel
32788     * @param {Node} this This node
32789     */
32790     this.addEvents({'beforeload':true, 'load': true});
32791     /**
32792     * @event load
32793     * Fires when this node is loaded
32794     * @param {Node} this This node
32795     */
32796     /**
32797      * The loader used by this node (defaults to using the tree's defined loader)
32798      * @type TreeLoader
32799      * @property loader
32800      */
32801 };
32802 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32803     expand : function(deep, anim, callback){
32804         if(this.loading){ // if an async load is already running, waiting til it's done
32805             var timer;
32806             var f = function(){
32807                 if(!this.loading){ // done loading
32808                     clearInterval(timer);
32809                     this.expand(deep, anim, callback);
32810                 }
32811             }.createDelegate(this);
32812             timer = setInterval(f, 200);
32813             return;
32814         }
32815         if(!this.loaded){
32816             if(this.fireEvent("beforeload", this) === false){
32817                 return;
32818             }
32819             this.loading = true;
32820             this.ui.beforeLoad(this);
32821             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32822             if(loader){
32823                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32824                 return;
32825             }
32826         }
32827         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32828     },
32829     
32830     /**
32831      * Returns true if this node is currently loading
32832      * @return {Boolean}
32833      */
32834     isLoading : function(){
32835         return this.loading;  
32836     },
32837     
32838     loadComplete : function(deep, anim, callback){
32839         this.loading = false;
32840         this.loaded = true;
32841         this.ui.afterLoad(this);
32842         this.fireEvent("load", this);
32843         this.expand(deep, anim, callback);
32844     },
32845     
32846     /**
32847      * Returns true if this node has been loaded
32848      * @return {Boolean}
32849      */
32850     isLoaded : function(){
32851         return this.loaded;
32852     },
32853     
32854     hasChildNodes : function(){
32855         if(!this.isLeaf() && !this.loaded){
32856             return true;
32857         }else{
32858             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32859         }
32860     },
32861
32862     /**
32863      * Trigger a reload for this node
32864      * @param {Function} callback
32865      */
32866     reload : function(callback){
32867         this.collapse(false, false);
32868         while(this.firstChild){
32869             this.removeChild(this.firstChild);
32870         }
32871         this.childrenRendered = false;
32872         this.loaded = false;
32873         if(this.isHiddenRoot()){
32874             this.expanded = false;
32875         }
32876         this.expand(false, false, callback);
32877     }
32878 });/*
32879  * Based on:
32880  * Ext JS Library 1.1.1
32881  * Copyright(c) 2006-2007, Ext JS, LLC.
32882  *
32883  * Originally Released Under LGPL - original licence link has changed is not relivant.
32884  *
32885  * Fork - LGPL
32886  * <script type="text/javascript">
32887  */
32888  
32889 /**
32890  * @class Roo.tree.TreeNodeUI
32891  * @constructor
32892  * @param {Object} node The node to render
32893  * The TreeNode UI implementation is separate from the
32894  * tree implementation. Unless you are customizing the tree UI,
32895  * you should never have to use this directly.
32896  */
32897 Roo.tree.TreeNodeUI = function(node){
32898     this.node = node;
32899     this.rendered = false;
32900     this.animating = false;
32901     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32902 };
32903
32904 Roo.tree.TreeNodeUI.prototype = {
32905     removeChild : function(node){
32906         if(this.rendered){
32907             this.ctNode.removeChild(node.ui.getEl());
32908         }
32909     },
32910
32911     beforeLoad : function(){
32912          this.addClass("x-tree-node-loading");
32913     },
32914
32915     afterLoad : function(){
32916          this.removeClass("x-tree-node-loading");
32917     },
32918
32919     onTextChange : function(node, text, oldText){
32920         if(this.rendered){
32921             this.textNode.innerHTML = text;
32922         }
32923     },
32924
32925     onDisableChange : function(node, state){
32926         this.disabled = state;
32927         if(state){
32928             this.addClass("x-tree-node-disabled");
32929         }else{
32930             this.removeClass("x-tree-node-disabled");
32931         }
32932     },
32933
32934     onSelectedChange : function(state){
32935         if(state){
32936             this.focus();
32937             this.addClass("x-tree-selected");
32938         }else{
32939             //this.blur();
32940             this.removeClass("x-tree-selected");
32941         }
32942     },
32943
32944     onMove : function(tree, node, oldParent, newParent, index, refNode){
32945         this.childIndent = null;
32946         if(this.rendered){
32947             var targetNode = newParent.ui.getContainer();
32948             if(!targetNode){//target not rendered
32949                 this.holder = document.createElement("div");
32950                 this.holder.appendChild(this.wrap);
32951                 return;
32952             }
32953             var insertBefore = refNode ? refNode.ui.getEl() : null;
32954             if(insertBefore){
32955                 targetNode.insertBefore(this.wrap, insertBefore);
32956             }else{
32957                 targetNode.appendChild(this.wrap);
32958             }
32959             this.node.renderIndent(true);
32960         }
32961     },
32962
32963     addClass : function(cls){
32964         if(this.elNode){
32965             Roo.fly(this.elNode).addClass(cls);
32966         }
32967     },
32968
32969     removeClass : function(cls){
32970         if(this.elNode){
32971             Roo.fly(this.elNode).removeClass(cls);
32972         }
32973     },
32974
32975     remove : function(){
32976         if(this.rendered){
32977             this.holder = document.createElement("div");
32978             this.holder.appendChild(this.wrap);
32979         }
32980     },
32981
32982     fireEvent : function(){
32983         return this.node.fireEvent.apply(this.node, arguments);
32984     },
32985
32986     initEvents : function(){
32987         this.node.on("move", this.onMove, this);
32988         var E = Roo.EventManager;
32989         var a = this.anchor;
32990
32991         var el = Roo.fly(a, '_treeui');
32992
32993         if(Roo.isOpera){ // opera render bug ignores the CSS
32994             el.setStyle("text-decoration", "none");
32995         }
32996
32997         el.on("click", this.onClick, this);
32998         el.on("dblclick", this.onDblClick, this);
32999
33000         if(this.checkbox){
33001             Roo.EventManager.on(this.checkbox,
33002                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33003         }
33004
33005         el.on("contextmenu", this.onContextMenu, this);
33006
33007         var icon = Roo.fly(this.iconNode);
33008         icon.on("click", this.onClick, this);
33009         icon.on("dblclick", this.onDblClick, this);
33010         icon.on("contextmenu", this.onContextMenu, this);
33011         E.on(this.ecNode, "click", this.ecClick, this, true);
33012
33013         if(this.node.disabled){
33014             this.addClass("x-tree-node-disabled");
33015         }
33016         if(this.node.hidden){
33017             this.addClass("x-tree-node-disabled");
33018         }
33019         var ot = this.node.getOwnerTree();
33020         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33021         if(dd && (!this.node.isRoot || ot.rootVisible)){
33022             Roo.dd.Registry.register(this.elNode, {
33023                 node: this.node,
33024                 handles: this.getDDHandles(),
33025                 isHandle: false
33026             });
33027         }
33028     },
33029
33030     getDDHandles : function(){
33031         return [this.iconNode, this.textNode];
33032     },
33033
33034     hide : function(){
33035         if(this.rendered){
33036             this.wrap.style.display = "none";
33037         }
33038     },
33039
33040     show : function(){
33041         if(this.rendered){
33042             this.wrap.style.display = "";
33043         }
33044     },
33045
33046     onContextMenu : function(e){
33047         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33048             e.preventDefault();
33049             this.focus();
33050             this.fireEvent("contextmenu", this.node, e);
33051         }
33052     },
33053
33054     onClick : function(e){
33055         if(this.dropping){
33056             e.stopEvent();
33057             return;
33058         }
33059         if(this.fireEvent("beforeclick", this.node, e) !== false){
33060             if(!this.disabled && this.node.attributes.href){
33061                 this.fireEvent("click", this.node, e);
33062                 return;
33063             }
33064             e.preventDefault();
33065             if(this.disabled){
33066                 return;
33067             }
33068
33069             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33070                 this.node.toggle();
33071             }
33072
33073             this.fireEvent("click", this.node, e);
33074         }else{
33075             e.stopEvent();
33076         }
33077     },
33078
33079     onDblClick : function(e){
33080         e.preventDefault();
33081         if(this.disabled){
33082             return;
33083         }
33084         if(this.checkbox){
33085             this.toggleCheck();
33086         }
33087         if(!this.animating && this.node.hasChildNodes()){
33088             this.node.toggle();
33089         }
33090         this.fireEvent("dblclick", this.node, e);
33091     },
33092
33093     onCheckChange : function(){
33094         var checked = this.checkbox.checked;
33095         this.node.attributes.checked = checked;
33096         this.fireEvent('checkchange', this.node, checked);
33097     },
33098
33099     ecClick : function(e){
33100         if(!this.animating && this.node.hasChildNodes()){
33101             this.node.toggle();
33102         }
33103     },
33104
33105     startDrop : function(){
33106         this.dropping = true;
33107     },
33108
33109     // delayed drop so the click event doesn't get fired on a drop
33110     endDrop : function(){
33111        setTimeout(function(){
33112            this.dropping = false;
33113        }.createDelegate(this), 50);
33114     },
33115
33116     expand : function(){
33117         this.updateExpandIcon();
33118         this.ctNode.style.display = "";
33119     },
33120
33121     focus : function(){
33122         if(!this.node.preventHScroll){
33123             try{this.anchor.focus();
33124             }catch(e){}
33125         }else if(!Roo.isIE){
33126             try{
33127                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33128                 var l = noscroll.scrollLeft;
33129                 this.anchor.focus();
33130                 noscroll.scrollLeft = l;
33131             }catch(e){}
33132         }
33133     },
33134
33135     toggleCheck : function(value){
33136         var cb = this.checkbox;
33137         if(cb){
33138             cb.checked = (value === undefined ? !cb.checked : value);
33139         }
33140     },
33141
33142     blur : function(){
33143         try{
33144             this.anchor.blur();
33145         }catch(e){}
33146     },
33147
33148     animExpand : function(callback){
33149         var ct = Roo.get(this.ctNode);
33150         ct.stopFx();
33151         if(!this.node.hasChildNodes()){
33152             this.updateExpandIcon();
33153             this.ctNode.style.display = "";
33154             Roo.callback(callback);
33155             return;
33156         }
33157         this.animating = true;
33158         this.updateExpandIcon();
33159
33160         ct.slideIn('t', {
33161            callback : function(){
33162                this.animating = false;
33163                Roo.callback(callback);
33164             },
33165             scope: this,
33166             duration: this.node.ownerTree.duration || .25
33167         });
33168     },
33169
33170     highlight : function(){
33171         var tree = this.node.getOwnerTree();
33172         Roo.fly(this.wrap).highlight(
33173             tree.hlColor || "C3DAF9",
33174             {endColor: tree.hlBaseColor}
33175         );
33176     },
33177
33178     collapse : function(){
33179         this.updateExpandIcon();
33180         this.ctNode.style.display = "none";
33181     },
33182
33183     animCollapse : function(callback){
33184         var ct = Roo.get(this.ctNode);
33185         ct.enableDisplayMode('block');
33186         ct.stopFx();
33187
33188         this.animating = true;
33189         this.updateExpandIcon();
33190
33191         ct.slideOut('t', {
33192             callback : function(){
33193                this.animating = false;
33194                Roo.callback(callback);
33195             },
33196             scope: this,
33197             duration: this.node.ownerTree.duration || .25
33198         });
33199     },
33200
33201     getContainer : function(){
33202         return this.ctNode;
33203     },
33204
33205     getEl : function(){
33206         return this.wrap;
33207     },
33208
33209     appendDDGhost : function(ghostNode){
33210         ghostNode.appendChild(this.elNode.cloneNode(true));
33211     },
33212
33213     getDDRepairXY : function(){
33214         return Roo.lib.Dom.getXY(this.iconNode);
33215     },
33216
33217     onRender : function(){
33218         this.render();
33219     },
33220
33221     render : function(bulkRender){
33222         var n = this.node, a = n.attributes;
33223         var targetNode = n.parentNode ?
33224               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33225
33226         if(!this.rendered){
33227             this.rendered = true;
33228
33229             this.renderElements(n, a, targetNode, bulkRender);
33230
33231             if(a.qtip){
33232                if(this.textNode.setAttributeNS){
33233                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33234                    if(a.qtipTitle){
33235                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33236                    }
33237                }else{
33238                    this.textNode.setAttribute("ext:qtip", a.qtip);
33239                    if(a.qtipTitle){
33240                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33241                    }
33242                }
33243             }else if(a.qtipCfg){
33244                 a.qtipCfg.target = Roo.id(this.textNode);
33245                 Roo.QuickTips.register(a.qtipCfg);
33246             }
33247             this.initEvents();
33248             if(!this.node.expanded){
33249                 this.updateExpandIcon();
33250             }
33251         }else{
33252             if(bulkRender === true) {
33253                 targetNode.appendChild(this.wrap);
33254             }
33255         }
33256     },
33257
33258     renderElements : function(n, a, targetNode, bulkRender)
33259     {
33260         // add some indent caching, this helps performance when rendering a large tree
33261         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33262         var t = n.getOwnerTree();
33263         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33264         if (typeof(n.attributes.html) != 'undefined') {
33265             txt = n.attributes.html;
33266         }
33267         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33268         var cb = typeof a.checked == 'boolean';
33269         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33270         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33271             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33272             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33273             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33274             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33275             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33276              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33277                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33278             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33279             "</li>"];
33280
33281         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33282             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33283                                 n.nextSibling.ui.getEl(), buf.join(""));
33284         }else{
33285             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33286         }
33287
33288         this.elNode = this.wrap.childNodes[0];
33289         this.ctNode = this.wrap.childNodes[1];
33290         var cs = this.elNode.childNodes;
33291         this.indentNode = cs[0];
33292         this.ecNode = cs[1];
33293         this.iconNode = cs[2];
33294         var index = 3;
33295         if(cb){
33296             this.checkbox = cs[3];
33297             index++;
33298         }
33299         this.anchor = cs[index];
33300         this.textNode = cs[index].firstChild;
33301     },
33302
33303     getAnchor : function(){
33304         return this.anchor;
33305     },
33306
33307     getTextEl : function(){
33308         return this.textNode;
33309     },
33310
33311     getIconEl : function(){
33312         return this.iconNode;
33313     },
33314
33315     isChecked : function(){
33316         return this.checkbox ? this.checkbox.checked : false;
33317     },
33318
33319     updateExpandIcon : function(){
33320         if(this.rendered){
33321             var n = this.node, c1, c2;
33322             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
33323             var hasChild = n.hasChildNodes();
33324             if(hasChild){
33325                 if(n.expanded){
33326                     cls += "-minus";
33327                     c1 = "x-tree-node-collapsed";
33328                     c2 = "x-tree-node-expanded";
33329                 }else{
33330                     cls += "-plus";
33331                     c1 = "x-tree-node-expanded";
33332                     c2 = "x-tree-node-collapsed";
33333                 }
33334                 if(this.wasLeaf){
33335                     this.removeClass("x-tree-node-leaf");
33336                     this.wasLeaf = false;
33337                 }
33338                 if(this.c1 != c1 || this.c2 != c2){
33339                     Roo.fly(this.elNode).replaceClass(c1, c2);
33340                     this.c1 = c1; this.c2 = c2;
33341                 }
33342             }else{
33343                 // this changes non-leafs into leafs if they have no children.
33344                 // it's not very rational behaviour..
33345                 
33346                 if(!this.wasLeaf && this.node.leaf){
33347                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
33348                     delete this.c1;
33349                     delete this.c2;
33350                     this.wasLeaf = true;
33351                 }
33352             }
33353             var ecc = "x-tree-ec-icon "+cls;
33354             if(this.ecc != ecc){
33355                 this.ecNode.className = ecc;
33356                 this.ecc = ecc;
33357             }
33358         }
33359     },
33360
33361     getChildIndent : function(){
33362         if(!this.childIndent){
33363             var buf = [];
33364             var p = this.node;
33365             while(p){
33366                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
33367                     if(!p.isLast()) {
33368                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
33369                     } else {
33370                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
33371                     }
33372                 }
33373                 p = p.parentNode;
33374             }
33375             this.childIndent = buf.join("");
33376         }
33377         return this.childIndent;
33378     },
33379
33380     renderIndent : function(){
33381         if(this.rendered){
33382             var indent = "";
33383             var p = this.node.parentNode;
33384             if(p){
33385                 indent = p.ui.getChildIndent();
33386             }
33387             if(this.indentMarkup != indent){ // don't rerender if not required
33388                 this.indentNode.innerHTML = indent;
33389                 this.indentMarkup = indent;
33390             }
33391             this.updateExpandIcon();
33392         }
33393     }
33394 };
33395
33396 Roo.tree.RootTreeNodeUI = function(){
33397     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
33398 };
33399 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
33400     render : function(){
33401         if(!this.rendered){
33402             var targetNode = this.node.ownerTree.innerCt.dom;
33403             this.node.expanded = true;
33404             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
33405             this.wrap = this.ctNode = targetNode.firstChild;
33406         }
33407     },
33408     collapse : function(){
33409     },
33410     expand : function(){
33411     }
33412 });/*
33413  * Based on:
33414  * Ext JS Library 1.1.1
33415  * Copyright(c) 2006-2007, Ext JS, LLC.
33416  *
33417  * Originally Released Under LGPL - original licence link has changed is not relivant.
33418  *
33419  * Fork - LGPL
33420  * <script type="text/javascript">
33421  */
33422 /**
33423  * @class Roo.tree.TreeLoader
33424  * @extends Roo.util.Observable
33425  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
33426  * nodes from a specified URL. The response must be a javascript Array definition
33427  * who's elements are node definition objects. eg:
33428  * <pre><code>
33429 {  success : true,
33430    data :      [
33431    
33432     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
33433     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
33434     ]
33435 }
33436
33437
33438 </code></pre>
33439  * <br><br>
33440  * The old style respose with just an array is still supported, but not recommended.
33441  * <br><br>
33442  *
33443  * A server request is sent, and child nodes are loaded only when a node is expanded.
33444  * The loading node's id is passed to the server under the parameter name "node" to
33445  * enable the server to produce the correct child nodes.
33446  * <br><br>
33447  * To pass extra parameters, an event handler may be attached to the "beforeload"
33448  * event, and the parameters specified in the TreeLoader's baseParams property:
33449  * <pre><code>
33450     myTreeLoader.on("beforeload", function(treeLoader, node) {
33451         this.baseParams.category = node.attributes.category;
33452     }, this);
33453 </code></pre><
33454  * This would pass an HTTP parameter called "category" to the server containing
33455  * the value of the Node's "category" attribute.
33456  * @constructor
33457  * Creates a new Treeloader.
33458  * @param {Object} config A config object containing config properties.
33459  */
33460 Roo.tree.TreeLoader = function(config){
33461     this.baseParams = {};
33462     this.requestMethod = "POST";
33463     Roo.apply(this, config);
33464
33465     this.addEvents({
33466     
33467         /**
33468          * @event beforeload
33469          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
33470          * @param {Object} This TreeLoader object.
33471          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33472          * @param {Object} callback The callback function specified in the {@link #load} call.
33473          */
33474         beforeload : true,
33475         /**
33476          * @event load
33477          * Fires when the node has been successfuly loaded.
33478          * @param {Object} This TreeLoader object.
33479          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33480          * @param {Object} response The response object containing the data from the server.
33481          */
33482         load : true,
33483         /**
33484          * @event loadexception
33485          * Fires if the network request failed.
33486          * @param {Object} This TreeLoader object.
33487          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33488          * @param {Object} response The response object containing the data from the server.
33489          */
33490         loadexception : true,
33491         /**
33492          * @event create
33493          * Fires before a node is created, enabling you to return custom Node types 
33494          * @param {Object} This TreeLoader object.
33495          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
33496          */
33497         create : true
33498     });
33499
33500     Roo.tree.TreeLoader.superclass.constructor.call(this);
33501 };
33502
33503 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
33504     /**
33505     * @cfg {String} dataUrl The URL from which to request a Json string which
33506     * specifies an array of node definition object representing the child nodes
33507     * to be loaded.
33508     */
33509     /**
33510     * @cfg {String} requestMethod either GET or POST
33511     * defaults to POST (due to BC)
33512     * to be loaded.
33513     */
33514     /**
33515     * @cfg {Object} baseParams (optional) An object containing properties which
33516     * specify HTTP parameters to be passed to each request for child nodes.
33517     */
33518     /**
33519     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
33520     * created by this loader. If the attributes sent by the server have an attribute in this object,
33521     * they take priority.
33522     */
33523     /**
33524     * @cfg {Object} uiProviders (optional) An object containing properties which
33525     * 
33526     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
33527     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
33528     * <i>uiProvider</i> attribute of a returned child node is a string rather
33529     * than a reference to a TreeNodeUI implementation, this that string value
33530     * is used as a property name in the uiProviders object. You can define the provider named
33531     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
33532     */
33533     uiProviders : {},
33534
33535     /**
33536     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
33537     * child nodes before loading.
33538     */
33539     clearOnLoad : true,
33540
33541     /**
33542     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
33543     * property on loading, rather than expecting an array. (eg. more compatible to a standard
33544     * Grid query { data : [ .....] }
33545     */
33546     
33547     root : false,
33548      /**
33549     * @cfg {String} queryParam (optional) 
33550     * Name of the query as it will be passed on the querystring (defaults to 'node')
33551     * eg. the request will be ?node=[id]
33552     */
33553     
33554     
33555     queryParam: false,
33556     
33557     /**
33558      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
33559      * This is called automatically when a node is expanded, but may be used to reload
33560      * a node (or append new children if the {@link #clearOnLoad} option is false.)
33561      * @param {Roo.tree.TreeNode} node
33562      * @param {Function} callback
33563      */
33564     load : function(node, callback){
33565         if(this.clearOnLoad){
33566             while(node.firstChild){
33567                 node.removeChild(node.firstChild);
33568             }
33569         }
33570         if(node.attributes.children){ // preloaded json children
33571             var cs = node.attributes.children;
33572             for(var i = 0, len = cs.length; i < len; i++){
33573                 node.appendChild(this.createNode(cs[i]));
33574             }
33575             if(typeof callback == "function"){
33576                 callback();
33577             }
33578         }else if(this.dataUrl){
33579             this.requestData(node, callback);
33580         }
33581     },
33582
33583     getParams: function(node){
33584         var buf = [], bp = this.baseParams;
33585         for(var key in bp){
33586             if(typeof bp[key] != "function"){
33587                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33588             }
33589         }
33590         var n = this.queryParam === false ? 'node' : this.queryParam;
33591         buf.push(n + "=", encodeURIComponent(node.id));
33592         return buf.join("");
33593     },
33594
33595     requestData : function(node, callback){
33596         if(this.fireEvent("beforeload", this, node, callback) !== false){
33597             this.transId = Roo.Ajax.request({
33598                 method:this.requestMethod,
33599                 url: this.dataUrl||this.url,
33600                 success: this.handleResponse,
33601                 failure: this.handleFailure,
33602                 scope: this,
33603                 argument: {callback: callback, node: node},
33604                 params: this.getParams(node)
33605             });
33606         }else{
33607             // if the load is cancelled, make sure we notify
33608             // the node that we are done
33609             if(typeof callback == "function"){
33610                 callback();
33611             }
33612         }
33613     },
33614
33615     isLoading : function(){
33616         return this.transId ? true : false;
33617     },
33618
33619     abort : function(){
33620         if(this.isLoading()){
33621             Roo.Ajax.abort(this.transId);
33622         }
33623     },
33624
33625     // private
33626     createNode : function(attr)
33627     {
33628         // apply baseAttrs, nice idea Corey!
33629         if(this.baseAttrs){
33630             Roo.applyIf(attr, this.baseAttrs);
33631         }
33632         if(this.applyLoader !== false){
33633             attr.loader = this;
33634         }
33635         // uiProvider = depreciated..
33636         
33637         if(typeof(attr.uiProvider) == 'string'){
33638            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33639                 /**  eval:var:attr */ eval(attr.uiProvider);
33640         }
33641         if(typeof(this.uiProviders['default']) != 'undefined') {
33642             attr.uiProvider = this.uiProviders['default'];
33643         }
33644         
33645         this.fireEvent('create', this, attr);
33646         
33647         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33648         return(attr.leaf ?
33649                         new Roo.tree.TreeNode(attr) :
33650                         new Roo.tree.AsyncTreeNode(attr));
33651     },
33652
33653     processResponse : function(response, node, callback)
33654     {
33655         var json = response.responseText;
33656         try {
33657             
33658             var o = Roo.decode(json);
33659             
33660             if (this.root === false && typeof(o.success) != undefined) {
33661                 this.root = 'data'; // the default behaviour for list like data..
33662                 }
33663                 
33664             if (this.root !== false &&  !o.success) {
33665                 // it's a failure condition.
33666                 var a = response.argument;
33667                 this.fireEvent("loadexception", this, a.node, response);
33668                 Roo.log("Load failed - should have a handler really");
33669                 return;
33670             }
33671             
33672             
33673             
33674             if (this.root !== false) {
33675                  o = o[this.root];
33676             }
33677             
33678             for(var i = 0, len = o.length; i < len; i++){
33679                 var n = this.createNode(o[i]);
33680                 if(n){
33681                     node.appendChild(n);
33682                 }
33683             }
33684             if(typeof callback == "function"){
33685                 callback(this, node);
33686             }
33687         }catch(e){
33688             this.handleFailure(response);
33689         }
33690     },
33691
33692     handleResponse : function(response){
33693         this.transId = false;
33694         var a = response.argument;
33695         this.processResponse(response, a.node, a.callback);
33696         this.fireEvent("load", this, a.node, response);
33697     },
33698
33699     handleFailure : function(response)
33700     {
33701         // should handle failure better..
33702         this.transId = false;
33703         var a = response.argument;
33704         this.fireEvent("loadexception", this, a.node, response);
33705         if(typeof a.callback == "function"){
33706             a.callback(this, a.node);
33707         }
33708     }
33709 });/*
33710  * Based on:
33711  * Ext JS Library 1.1.1
33712  * Copyright(c) 2006-2007, Ext JS, LLC.
33713  *
33714  * Originally Released Under LGPL - original licence link has changed is not relivant.
33715  *
33716  * Fork - LGPL
33717  * <script type="text/javascript">
33718  */
33719
33720 /**
33721 * @class Roo.tree.TreeFilter
33722 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33723 * @param {TreePanel} tree
33724 * @param {Object} config (optional)
33725  */
33726 Roo.tree.TreeFilter = function(tree, config){
33727     this.tree = tree;
33728     this.filtered = {};
33729     Roo.apply(this, config);
33730 };
33731
33732 Roo.tree.TreeFilter.prototype = {
33733     clearBlank:false,
33734     reverse:false,
33735     autoClear:false,
33736     remove:false,
33737
33738      /**
33739      * Filter the data by a specific attribute.
33740      * @param {String/RegExp} value Either string that the attribute value
33741      * should start with or a RegExp to test against the attribute
33742      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33743      * @param {TreeNode} startNode (optional) The node to start the filter at.
33744      */
33745     filter : function(value, attr, startNode){
33746         attr = attr || "text";
33747         var f;
33748         if(typeof value == "string"){
33749             var vlen = value.length;
33750             // auto clear empty filter
33751             if(vlen == 0 && this.clearBlank){
33752                 this.clear();
33753                 return;
33754             }
33755             value = value.toLowerCase();
33756             f = function(n){
33757                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33758             };
33759         }else if(value.exec){ // regex?
33760             f = function(n){
33761                 return value.test(n.attributes[attr]);
33762             };
33763         }else{
33764             throw 'Illegal filter type, must be string or regex';
33765         }
33766         this.filterBy(f, null, startNode);
33767         },
33768
33769     /**
33770      * Filter by a function. The passed function will be called with each
33771      * node in the tree (or from the startNode). If the function returns true, the node is kept
33772      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33773      * @param {Function} fn The filter function
33774      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33775      */
33776     filterBy : function(fn, scope, startNode){
33777         startNode = startNode || this.tree.root;
33778         if(this.autoClear){
33779             this.clear();
33780         }
33781         var af = this.filtered, rv = this.reverse;
33782         var f = function(n){
33783             if(n == startNode){
33784                 return true;
33785             }
33786             if(af[n.id]){
33787                 return false;
33788             }
33789             var m = fn.call(scope || n, n);
33790             if(!m || rv){
33791                 af[n.id] = n;
33792                 n.ui.hide();
33793                 return false;
33794             }
33795             return true;
33796         };
33797         startNode.cascade(f);
33798         if(this.remove){
33799            for(var id in af){
33800                if(typeof id != "function"){
33801                    var n = af[id];
33802                    if(n && n.parentNode){
33803                        n.parentNode.removeChild(n);
33804                    }
33805                }
33806            }
33807         }
33808     },
33809
33810     /**
33811      * Clears the current filter. Note: with the "remove" option
33812      * set a filter cannot be cleared.
33813      */
33814     clear : function(){
33815         var t = this.tree;
33816         var af = this.filtered;
33817         for(var id in af){
33818             if(typeof id != "function"){
33819                 var n = af[id];
33820                 if(n){
33821                     n.ui.show();
33822                 }
33823             }
33824         }
33825         this.filtered = {};
33826     }
33827 };
33828 /*
33829  * Based on:
33830  * Ext JS Library 1.1.1
33831  * Copyright(c) 2006-2007, Ext JS, LLC.
33832  *
33833  * Originally Released Under LGPL - original licence link has changed is not relivant.
33834  *
33835  * Fork - LGPL
33836  * <script type="text/javascript">
33837  */
33838  
33839
33840 /**
33841  * @class Roo.tree.TreeSorter
33842  * Provides sorting of nodes in a TreePanel
33843  * 
33844  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33845  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33846  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33847  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33848  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33849  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33850  * @constructor
33851  * @param {TreePanel} tree
33852  * @param {Object} config
33853  */
33854 Roo.tree.TreeSorter = function(tree, config){
33855     Roo.apply(this, config);
33856     tree.on("beforechildrenrendered", this.doSort, this);
33857     tree.on("append", this.updateSort, this);
33858     tree.on("insert", this.updateSort, this);
33859     
33860     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33861     var p = this.property || "text";
33862     var sortType = this.sortType;
33863     var fs = this.folderSort;
33864     var cs = this.caseSensitive === true;
33865     var leafAttr = this.leafAttr || 'leaf';
33866
33867     this.sortFn = function(n1, n2){
33868         if(fs){
33869             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33870                 return 1;
33871             }
33872             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33873                 return -1;
33874             }
33875         }
33876         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33877         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33878         if(v1 < v2){
33879                         return dsc ? +1 : -1;
33880                 }else if(v1 > v2){
33881                         return dsc ? -1 : +1;
33882         }else{
33883                 return 0;
33884         }
33885     };
33886 };
33887
33888 Roo.tree.TreeSorter.prototype = {
33889     doSort : function(node){
33890         node.sort(this.sortFn);
33891     },
33892     
33893     compareNodes : function(n1, n2){
33894         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33895     },
33896     
33897     updateSort : function(tree, node){
33898         if(node.childrenRendered){
33899             this.doSort.defer(1, this, [node]);
33900         }
33901     }
33902 };/*
33903  * Based on:
33904  * Ext JS Library 1.1.1
33905  * Copyright(c) 2006-2007, Ext JS, LLC.
33906  *
33907  * Originally Released Under LGPL - original licence link has changed is not relivant.
33908  *
33909  * Fork - LGPL
33910  * <script type="text/javascript">
33911  */
33912
33913 if(Roo.dd.DropZone){
33914     
33915 Roo.tree.TreeDropZone = function(tree, config){
33916     this.allowParentInsert = false;
33917     this.allowContainerDrop = false;
33918     this.appendOnly = false;
33919     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33920     this.tree = tree;
33921     this.lastInsertClass = "x-tree-no-status";
33922     this.dragOverData = {};
33923 };
33924
33925 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33926     ddGroup : "TreeDD",
33927     scroll:  true,
33928     
33929     expandDelay : 1000,
33930     
33931     expandNode : function(node){
33932         if(node.hasChildNodes() && !node.isExpanded()){
33933             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33934         }
33935     },
33936     
33937     queueExpand : function(node){
33938         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33939     },
33940     
33941     cancelExpand : function(){
33942         if(this.expandProcId){
33943             clearTimeout(this.expandProcId);
33944             this.expandProcId = false;
33945         }
33946     },
33947     
33948     isValidDropPoint : function(n, pt, dd, e, data){
33949         if(!n || !data){ return false; }
33950         var targetNode = n.node;
33951         var dropNode = data.node;
33952         // default drop rules
33953         if(!(targetNode && targetNode.isTarget && pt)){
33954             return false;
33955         }
33956         if(pt == "append" && targetNode.allowChildren === false){
33957             return false;
33958         }
33959         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33960             return false;
33961         }
33962         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33963             return false;
33964         }
33965         // reuse the object
33966         var overEvent = this.dragOverData;
33967         overEvent.tree = this.tree;
33968         overEvent.target = targetNode;
33969         overEvent.data = data;
33970         overEvent.point = pt;
33971         overEvent.source = dd;
33972         overEvent.rawEvent = e;
33973         overEvent.dropNode = dropNode;
33974         overEvent.cancel = false;  
33975         var result = this.tree.fireEvent("nodedragover", overEvent);
33976         return overEvent.cancel === false && result !== false;
33977     },
33978     
33979     getDropPoint : function(e, n, dd)
33980     {
33981         var tn = n.node;
33982         if(tn.isRoot){
33983             return tn.allowChildren !== false ? "append" : false; // always append for root
33984         }
33985         var dragEl = n.ddel;
33986         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33987         var y = Roo.lib.Event.getPageY(e);
33988         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33989         
33990         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33991         var noAppend = tn.allowChildren === false;
33992         if(this.appendOnly || tn.parentNode.allowChildren === false){
33993             return noAppend ? false : "append";
33994         }
33995         var noBelow = false;
33996         if(!this.allowParentInsert){
33997             noBelow = tn.hasChildNodes() && tn.isExpanded();
33998         }
33999         var q = (b - t) / (noAppend ? 2 : 3);
34000         if(y >= t && y < (t + q)){
34001             return "above";
34002         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34003             return "below";
34004         }else{
34005             return "append";
34006         }
34007     },
34008     
34009     onNodeEnter : function(n, dd, e, data)
34010     {
34011         this.cancelExpand();
34012     },
34013     
34014     onNodeOver : function(n, dd, e, data)
34015     {
34016        
34017         var pt = this.getDropPoint(e, n, dd);
34018         var node = n.node;
34019         
34020         // auto node expand check
34021         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34022             this.queueExpand(node);
34023         }else if(pt != "append"){
34024             this.cancelExpand();
34025         }
34026         
34027         // set the insert point style on the target node
34028         var returnCls = this.dropNotAllowed;
34029         if(this.isValidDropPoint(n, pt, dd, e, data)){
34030            if(pt){
34031                var el = n.ddel;
34032                var cls;
34033                if(pt == "above"){
34034                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34035                    cls = "x-tree-drag-insert-above";
34036                }else if(pt == "below"){
34037                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34038                    cls = "x-tree-drag-insert-below";
34039                }else{
34040                    returnCls = "x-tree-drop-ok-append";
34041                    cls = "x-tree-drag-append";
34042                }
34043                if(this.lastInsertClass != cls){
34044                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34045                    this.lastInsertClass = cls;
34046                }
34047            }
34048        }
34049        return returnCls;
34050     },
34051     
34052     onNodeOut : function(n, dd, e, data){
34053         
34054         this.cancelExpand();
34055         this.removeDropIndicators(n);
34056     },
34057     
34058     onNodeDrop : function(n, dd, e, data){
34059         var point = this.getDropPoint(e, n, dd);
34060         var targetNode = n.node;
34061         targetNode.ui.startDrop();
34062         if(!this.isValidDropPoint(n, point, dd, e, data)){
34063             targetNode.ui.endDrop();
34064             return false;
34065         }
34066         // first try to find the drop node
34067         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34068         var dropEvent = {
34069             tree : this.tree,
34070             target: targetNode,
34071             data: data,
34072             point: point,
34073             source: dd,
34074             rawEvent: e,
34075             dropNode: dropNode,
34076             cancel: !dropNode   
34077         };
34078         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34079         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34080             targetNode.ui.endDrop();
34081             return false;
34082         }
34083         // allow target changing
34084         targetNode = dropEvent.target;
34085         if(point == "append" && !targetNode.isExpanded()){
34086             targetNode.expand(false, null, function(){
34087                 this.completeDrop(dropEvent);
34088             }.createDelegate(this));
34089         }else{
34090             this.completeDrop(dropEvent);
34091         }
34092         return true;
34093     },
34094     
34095     completeDrop : function(de){
34096         var ns = de.dropNode, p = de.point, t = de.target;
34097         if(!(ns instanceof Array)){
34098             ns = [ns];
34099         }
34100         var n;
34101         for(var i = 0, len = ns.length; i < len; i++){
34102             n = ns[i];
34103             if(p == "above"){
34104                 t.parentNode.insertBefore(n, t);
34105             }else if(p == "below"){
34106                 t.parentNode.insertBefore(n, t.nextSibling);
34107             }else{
34108                 t.appendChild(n);
34109             }
34110         }
34111         n.ui.focus();
34112         if(this.tree.hlDrop){
34113             n.ui.highlight();
34114         }
34115         t.ui.endDrop();
34116         this.tree.fireEvent("nodedrop", de);
34117     },
34118     
34119     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34120         if(this.tree.hlDrop){
34121             dropNode.ui.focus();
34122             dropNode.ui.highlight();
34123         }
34124         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34125     },
34126     
34127     getTree : function(){
34128         return this.tree;
34129     },
34130     
34131     removeDropIndicators : function(n){
34132         if(n && n.ddel){
34133             var el = n.ddel;
34134             Roo.fly(el).removeClass([
34135                     "x-tree-drag-insert-above",
34136                     "x-tree-drag-insert-below",
34137                     "x-tree-drag-append"]);
34138             this.lastInsertClass = "_noclass";
34139         }
34140     },
34141     
34142     beforeDragDrop : function(target, e, id){
34143         this.cancelExpand();
34144         return true;
34145     },
34146     
34147     afterRepair : function(data){
34148         if(data && Roo.enableFx){
34149             data.node.ui.highlight();
34150         }
34151         this.hideProxy();
34152     } 
34153     
34154 });
34155
34156 }
34157 /*
34158  * Based on:
34159  * Ext JS Library 1.1.1
34160  * Copyright(c) 2006-2007, Ext JS, LLC.
34161  *
34162  * Originally Released Under LGPL - original licence link has changed is not relivant.
34163  *
34164  * Fork - LGPL
34165  * <script type="text/javascript">
34166  */
34167  
34168
34169 if(Roo.dd.DragZone){
34170 Roo.tree.TreeDragZone = function(tree, config){
34171     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34172     this.tree = tree;
34173 };
34174
34175 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34176     ddGroup : "TreeDD",
34177    
34178     onBeforeDrag : function(data, e){
34179         var n = data.node;
34180         return n && n.draggable && !n.disabled;
34181     },
34182      
34183     
34184     onInitDrag : function(e){
34185         var data = this.dragData;
34186         this.tree.getSelectionModel().select(data.node);
34187         this.proxy.update("");
34188         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34189         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34190     },
34191     
34192     getRepairXY : function(e, data){
34193         return data.node.ui.getDDRepairXY();
34194     },
34195     
34196     onEndDrag : function(data, e){
34197         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34198         
34199         
34200     },
34201     
34202     onValidDrop : function(dd, e, id){
34203         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34204         this.hideProxy();
34205     },
34206     
34207     beforeInvalidDrop : function(e, id){
34208         // this scrolls the original position back into view
34209         var sm = this.tree.getSelectionModel();
34210         sm.clearSelections();
34211         sm.select(this.dragData.node);
34212     }
34213 });
34214 }/*
34215  * Based on:
34216  * Ext JS Library 1.1.1
34217  * Copyright(c) 2006-2007, Ext JS, LLC.
34218  *
34219  * Originally Released Under LGPL - original licence link has changed is not relivant.
34220  *
34221  * Fork - LGPL
34222  * <script type="text/javascript">
34223  */
34224 /**
34225  * @class Roo.tree.TreeEditor
34226  * @extends Roo.Editor
34227  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34228  * as the editor field.
34229  * @constructor
34230  * @param {Object} config (used to be the tree panel.)
34231  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34232  * 
34233  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34234  * @cfg {Roo.form.TextField|Object} field The field configuration
34235  *
34236  * 
34237  */
34238 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34239     var tree = config;
34240     var field;
34241     if (oldconfig) { // old style..
34242         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34243     } else {
34244         // new style..
34245         tree = config.tree;
34246         config.field = config.field  || {};
34247         config.field.xtype = 'TextField';
34248         field = Roo.factory(config.field, Roo.form);
34249     }
34250     config = config || {};
34251     
34252     
34253     this.addEvents({
34254         /**
34255          * @event beforenodeedit
34256          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34257          * false from the handler of this event.
34258          * @param {Editor} this
34259          * @param {Roo.tree.Node} node 
34260          */
34261         "beforenodeedit" : true
34262     });
34263     
34264     //Roo.log(config);
34265     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34266
34267     this.tree = tree;
34268
34269     tree.on('beforeclick', this.beforeNodeClick, this);
34270     tree.getTreeEl().on('mousedown', this.hide, this);
34271     this.on('complete', this.updateNode, this);
34272     this.on('beforestartedit', this.fitToTree, this);
34273     this.on('startedit', this.bindScroll, this, {delay:10});
34274     this.on('specialkey', this.onSpecialKey, this);
34275 };
34276
34277 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34278     /**
34279      * @cfg {String} alignment
34280      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34281      */
34282     alignment: "l-l",
34283     // inherit
34284     autoSize: false,
34285     /**
34286      * @cfg {Boolean} hideEl
34287      * True to hide the bound element while the editor is displayed (defaults to false)
34288      */
34289     hideEl : false,
34290     /**
34291      * @cfg {String} cls
34292      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34293      */
34294     cls: "x-small-editor x-tree-editor",
34295     /**
34296      * @cfg {Boolean} shim
34297      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34298      */
34299     shim:false,
34300     // inherit
34301     shadow:"frame",
34302     /**
34303      * @cfg {Number} maxWidth
34304      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34305      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34306      * scroll and client offsets into account prior to each edit.
34307      */
34308     maxWidth: 250,
34309
34310     editDelay : 350,
34311
34312     // private
34313     fitToTree : function(ed, el){
34314         var td = this.tree.getTreeEl().dom, nd = el.dom;
34315         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34316             td.scrollLeft = nd.offsetLeft;
34317         }
34318         var w = Math.min(
34319                 this.maxWidth,
34320                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
34321         this.setSize(w, '');
34322         
34323         return this.fireEvent('beforenodeedit', this, this.editNode);
34324         
34325     },
34326
34327     // private
34328     triggerEdit : function(node){
34329         this.completeEdit();
34330         this.editNode = node;
34331         this.startEdit(node.ui.textNode, node.text);
34332     },
34333
34334     // private
34335     bindScroll : function(){
34336         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
34337     },
34338
34339     // private
34340     beforeNodeClick : function(node, e){
34341         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
34342         this.lastClick = new Date();
34343         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
34344             e.stopEvent();
34345             this.triggerEdit(node);
34346             return false;
34347         }
34348         return true;
34349     },
34350
34351     // private
34352     updateNode : function(ed, value){
34353         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
34354         this.editNode.setText(value);
34355     },
34356
34357     // private
34358     onHide : function(){
34359         Roo.tree.TreeEditor.superclass.onHide.call(this);
34360         if(this.editNode){
34361             this.editNode.ui.focus();
34362         }
34363     },
34364
34365     // private
34366     onSpecialKey : function(field, e){
34367         var k = e.getKey();
34368         if(k == e.ESC){
34369             e.stopEvent();
34370             this.cancelEdit();
34371         }else if(k == e.ENTER && !e.hasModifier()){
34372             e.stopEvent();
34373             this.completeEdit();
34374         }
34375     }
34376 });//<Script type="text/javascript">
34377 /*
34378  * Based on:
34379  * Ext JS Library 1.1.1
34380  * Copyright(c) 2006-2007, Ext JS, LLC.
34381  *
34382  * Originally Released Under LGPL - original licence link has changed is not relivant.
34383  *
34384  * Fork - LGPL
34385  * <script type="text/javascript">
34386  */
34387  
34388 /**
34389  * Not documented??? - probably should be...
34390  */
34391
34392 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
34393     //focus: Roo.emptyFn, // prevent odd scrolling behavior
34394     
34395     renderElements : function(n, a, targetNode, bulkRender){
34396         //consel.log("renderElements?");
34397         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34398
34399         var t = n.getOwnerTree();
34400         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
34401         
34402         var cols = t.columns;
34403         var bw = t.borderWidth;
34404         var c = cols[0];
34405         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34406          var cb = typeof a.checked == "boolean";
34407         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34408         var colcls = 'x-t-' + tid + '-c0';
34409         var buf = [
34410             '<li class="x-tree-node">',
34411             
34412                 
34413                 '<div class="x-tree-node-el ', a.cls,'">',
34414                     // extran...
34415                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
34416                 
34417                 
34418                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
34419                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
34420                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
34421                            (a.icon ? ' x-tree-node-inline-icon' : ''),
34422                            (a.iconCls ? ' '+a.iconCls : ''),
34423                            '" unselectable="on" />',
34424                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
34425                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
34426                              
34427                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34428                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
34429                             '<span unselectable="on" qtip="' + tx + '">',
34430                              tx,
34431                              '</span></a>' ,
34432                     '</div>',
34433                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34434                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
34435                  ];
34436         for(var i = 1, len = cols.length; i < len; i++){
34437             c = cols[i];
34438             colcls = 'x-t-' + tid + '-c' +i;
34439             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34440             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
34441                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
34442                       "</div>");
34443          }
34444          
34445          buf.push(
34446             '</a>',
34447             '<div class="x-clear"></div></div>',
34448             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34449             "</li>");
34450         
34451         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34452             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34453                                 n.nextSibling.ui.getEl(), buf.join(""));
34454         }else{
34455             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34456         }
34457         var el = this.wrap.firstChild;
34458         this.elRow = el;
34459         this.elNode = el.firstChild;
34460         this.ranchor = el.childNodes[1];
34461         this.ctNode = this.wrap.childNodes[1];
34462         var cs = el.firstChild.childNodes;
34463         this.indentNode = cs[0];
34464         this.ecNode = cs[1];
34465         this.iconNode = cs[2];
34466         var index = 3;
34467         if(cb){
34468             this.checkbox = cs[3];
34469             index++;
34470         }
34471         this.anchor = cs[index];
34472         
34473         this.textNode = cs[index].firstChild;
34474         
34475         //el.on("click", this.onClick, this);
34476         //el.on("dblclick", this.onDblClick, this);
34477         
34478         
34479        // console.log(this);
34480     },
34481     initEvents : function(){
34482         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
34483         
34484             
34485         var a = this.ranchor;
34486
34487         var el = Roo.get(a);
34488
34489         if(Roo.isOpera){ // opera render bug ignores the CSS
34490             el.setStyle("text-decoration", "none");
34491         }
34492
34493         el.on("click", this.onClick, this);
34494         el.on("dblclick", this.onDblClick, this);
34495         el.on("contextmenu", this.onContextMenu, this);
34496         
34497     },
34498     
34499     /*onSelectedChange : function(state){
34500         if(state){
34501             this.focus();
34502             this.addClass("x-tree-selected");
34503         }else{
34504             //this.blur();
34505             this.removeClass("x-tree-selected");
34506         }
34507     },*/
34508     addClass : function(cls){
34509         if(this.elRow){
34510             Roo.fly(this.elRow).addClass(cls);
34511         }
34512         
34513     },
34514     
34515     
34516     removeClass : function(cls){
34517         if(this.elRow){
34518             Roo.fly(this.elRow).removeClass(cls);
34519         }
34520     }
34521
34522     
34523     
34524 });//<Script type="text/javascript">
34525
34526 /*
34527  * Based on:
34528  * Ext JS Library 1.1.1
34529  * Copyright(c) 2006-2007, Ext JS, LLC.
34530  *
34531  * Originally Released Under LGPL - original licence link has changed is not relivant.
34532  *
34533  * Fork - LGPL
34534  * <script type="text/javascript">
34535  */
34536  
34537
34538 /**
34539  * @class Roo.tree.ColumnTree
34540  * @extends Roo.data.TreePanel
34541  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
34542  * @cfg {int} borderWidth  compined right/left border allowance
34543  * @constructor
34544  * @param {String/HTMLElement/Element} el The container element
34545  * @param {Object} config
34546  */
34547 Roo.tree.ColumnTree =  function(el, config)
34548 {
34549    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
34550    this.addEvents({
34551         /**
34552         * @event resize
34553         * Fire this event on a container when it resizes
34554         * @param {int} w Width
34555         * @param {int} h Height
34556         */
34557        "resize" : true
34558     });
34559     this.on('resize', this.onResize, this);
34560 };
34561
34562 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
34563     //lines:false,
34564     
34565     
34566     borderWidth: Roo.isBorderBox ? 0 : 2, 
34567     headEls : false,
34568     
34569     render : function(){
34570         // add the header.....
34571        
34572         Roo.tree.ColumnTree.superclass.render.apply(this);
34573         
34574         this.el.addClass('x-column-tree');
34575         
34576         this.headers = this.el.createChild(
34577             {cls:'x-tree-headers'},this.innerCt.dom);
34578    
34579         var cols = this.columns, c;
34580         var totalWidth = 0;
34581         this.headEls = [];
34582         var  len = cols.length;
34583         for(var i = 0; i < len; i++){
34584              c = cols[i];
34585              totalWidth += c.width;
34586             this.headEls.push(this.headers.createChild({
34587                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
34588                  cn: {
34589                      cls:'x-tree-hd-text',
34590                      html: c.header
34591                  },
34592                  style:'width:'+(c.width-this.borderWidth)+'px;'
34593              }));
34594         }
34595         this.headers.createChild({cls:'x-clear'});
34596         // prevent floats from wrapping when clipped
34597         this.headers.setWidth(totalWidth);
34598         //this.innerCt.setWidth(totalWidth);
34599         this.innerCt.setStyle({ overflow: 'auto' });
34600         this.onResize(this.width, this.height);
34601              
34602         
34603     },
34604     onResize : function(w,h)
34605     {
34606         this.height = h;
34607         this.width = w;
34608         // resize cols..
34609         this.innerCt.setWidth(this.width);
34610         this.innerCt.setHeight(this.height-20);
34611         
34612         // headers...
34613         var cols = this.columns, c;
34614         var totalWidth = 0;
34615         var expEl = false;
34616         var len = cols.length;
34617         for(var i = 0; i < len; i++){
34618             c = cols[i];
34619             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34620                 // it's the expander..
34621                 expEl  = this.headEls[i];
34622                 continue;
34623             }
34624             totalWidth += c.width;
34625             
34626         }
34627         if (expEl) {
34628             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34629         }
34630         this.headers.setWidth(w-20);
34631
34632         
34633         
34634         
34635     }
34636 });
34637 /*
34638  * Based on:
34639  * Ext JS Library 1.1.1
34640  * Copyright(c) 2006-2007, Ext JS, LLC.
34641  *
34642  * Originally Released Under LGPL - original licence link has changed is not relivant.
34643  *
34644  * Fork - LGPL
34645  * <script type="text/javascript">
34646  */
34647  
34648 /**
34649  * @class Roo.menu.Menu
34650  * @extends Roo.util.Observable
34651  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34652  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34653  * @constructor
34654  * Creates a new Menu
34655  * @param {Object} config Configuration options
34656  */
34657 Roo.menu.Menu = function(config){
34658     Roo.apply(this, config);
34659     this.id = this.id || Roo.id();
34660     this.addEvents({
34661         /**
34662          * @event beforeshow
34663          * Fires before this menu is displayed
34664          * @param {Roo.menu.Menu} this
34665          */
34666         beforeshow : true,
34667         /**
34668          * @event beforehide
34669          * Fires before this menu is hidden
34670          * @param {Roo.menu.Menu} this
34671          */
34672         beforehide : true,
34673         /**
34674          * @event show
34675          * Fires after this menu is displayed
34676          * @param {Roo.menu.Menu} this
34677          */
34678         show : true,
34679         /**
34680          * @event hide
34681          * Fires after this menu is hidden
34682          * @param {Roo.menu.Menu} this
34683          */
34684         hide : true,
34685         /**
34686          * @event click
34687          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34688          * @param {Roo.menu.Menu} this
34689          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34690          * @param {Roo.EventObject} e
34691          */
34692         click : true,
34693         /**
34694          * @event mouseover
34695          * Fires when the mouse is hovering over this menu
34696          * @param {Roo.menu.Menu} this
34697          * @param {Roo.EventObject} e
34698          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34699          */
34700         mouseover : true,
34701         /**
34702          * @event mouseout
34703          * Fires when the mouse exits this menu
34704          * @param {Roo.menu.Menu} this
34705          * @param {Roo.EventObject} e
34706          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34707          */
34708         mouseout : true,
34709         /**
34710          * @event itemclick
34711          * Fires when a menu item contained in this menu is clicked
34712          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34713          * @param {Roo.EventObject} e
34714          */
34715         itemclick: true
34716     });
34717     if (this.registerMenu) {
34718         Roo.menu.MenuMgr.register(this);
34719     }
34720     
34721     var mis = this.items;
34722     this.items = new Roo.util.MixedCollection();
34723     if(mis){
34724         this.add.apply(this, mis);
34725     }
34726 };
34727
34728 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34729     /**
34730      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34731      */
34732     minWidth : 120,
34733     /**
34734      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34735      * for bottom-right shadow (defaults to "sides")
34736      */
34737     shadow : "sides",
34738     /**
34739      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34740      * this menu (defaults to "tl-tr?")
34741      */
34742     subMenuAlign : "tl-tr?",
34743     /**
34744      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34745      * relative to its element of origin (defaults to "tl-bl?")
34746      */
34747     defaultAlign : "tl-bl?",
34748     /**
34749      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34750      */
34751     allowOtherMenus : false,
34752     /**
34753      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34754      */
34755     registerMenu : true,
34756
34757     hidden:true,
34758
34759     // private
34760     render : function(){
34761         if(this.el){
34762             return;
34763         }
34764         var el = this.el = new Roo.Layer({
34765             cls: "x-menu",
34766             shadow:this.shadow,
34767             constrain: false,
34768             parentEl: this.parentEl || document.body,
34769             zindex:15000
34770         });
34771
34772         this.keyNav = new Roo.menu.MenuNav(this);
34773
34774         if(this.plain){
34775             el.addClass("x-menu-plain");
34776         }
34777         if(this.cls){
34778             el.addClass(this.cls);
34779         }
34780         // generic focus element
34781         this.focusEl = el.createChild({
34782             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34783         });
34784         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34785         ul.on("click", this.onClick, this);
34786         ul.on("mouseover", this.onMouseOver, this);
34787         ul.on("mouseout", this.onMouseOut, this);
34788         this.items.each(function(item){
34789             var li = document.createElement("li");
34790             li.className = "x-menu-list-item";
34791             ul.dom.appendChild(li);
34792             item.render(li, this);
34793         }, this);
34794         this.ul = ul;
34795         this.autoWidth();
34796     },
34797
34798     // private
34799     autoWidth : function(){
34800         var el = this.el, ul = this.ul;
34801         if(!el){
34802             return;
34803         }
34804         var w = this.width;
34805         if(w){
34806             el.setWidth(w);
34807         }else if(Roo.isIE){
34808             el.setWidth(this.minWidth);
34809             var t = el.dom.offsetWidth; // force recalc
34810             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34811         }
34812     },
34813
34814     // private
34815     delayAutoWidth : function(){
34816         if(this.rendered){
34817             if(!this.awTask){
34818                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34819             }
34820             this.awTask.delay(20);
34821         }
34822     },
34823
34824     // private
34825     findTargetItem : function(e){
34826         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34827         if(t && t.menuItemId){
34828             return this.items.get(t.menuItemId);
34829         }
34830     },
34831
34832     // private
34833     onClick : function(e){
34834         var t;
34835         if(t = this.findTargetItem(e)){
34836             t.onClick(e);
34837             this.fireEvent("click", this, t, e);
34838         }
34839     },
34840
34841     // private
34842     setActiveItem : function(item, autoExpand){
34843         if(item != this.activeItem){
34844             if(this.activeItem){
34845                 this.activeItem.deactivate();
34846             }
34847             this.activeItem = item;
34848             item.activate(autoExpand);
34849         }else if(autoExpand){
34850             item.expandMenu();
34851         }
34852     },
34853
34854     // private
34855     tryActivate : function(start, step){
34856         var items = this.items;
34857         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34858             var item = items.get(i);
34859             if(!item.disabled && item.canActivate){
34860                 this.setActiveItem(item, false);
34861                 return item;
34862             }
34863         }
34864         return false;
34865     },
34866
34867     // private
34868     onMouseOver : function(e){
34869         var t;
34870         if(t = this.findTargetItem(e)){
34871             if(t.canActivate && !t.disabled){
34872                 this.setActiveItem(t, true);
34873             }
34874         }
34875         this.fireEvent("mouseover", this, e, t);
34876     },
34877
34878     // private
34879     onMouseOut : function(e){
34880         var t;
34881         if(t = this.findTargetItem(e)){
34882             if(t == this.activeItem && t.shouldDeactivate(e)){
34883                 this.activeItem.deactivate();
34884                 delete this.activeItem;
34885             }
34886         }
34887         this.fireEvent("mouseout", this, e, t);
34888     },
34889
34890     /**
34891      * Read-only.  Returns true if the menu is currently displayed, else false.
34892      * @type Boolean
34893      */
34894     isVisible : function(){
34895         return this.el && !this.hidden;
34896     },
34897
34898     /**
34899      * Displays this menu relative to another element
34900      * @param {String/HTMLElement/Roo.Element} element The element to align to
34901      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34902      * the element (defaults to this.defaultAlign)
34903      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34904      */
34905     show : function(el, pos, parentMenu){
34906         this.parentMenu = parentMenu;
34907         if(!this.el){
34908             this.render();
34909         }
34910         this.fireEvent("beforeshow", this);
34911         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34912     },
34913
34914     /**
34915      * Displays this menu at a specific xy position
34916      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34917      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34918      */
34919     showAt : function(xy, parentMenu, /* private: */_e){
34920         this.parentMenu = parentMenu;
34921         if(!this.el){
34922             this.render();
34923         }
34924         if(_e !== false){
34925             this.fireEvent("beforeshow", this);
34926             xy = this.el.adjustForConstraints(xy);
34927         }
34928         this.el.setXY(xy);
34929         this.el.show();
34930         this.hidden = false;
34931         this.focus();
34932         this.fireEvent("show", this);
34933     },
34934
34935     focus : function(){
34936         if(!this.hidden){
34937             this.doFocus.defer(50, this);
34938         }
34939     },
34940
34941     doFocus : function(){
34942         if(!this.hidden){
34943             this.focusEl.focus();
34944         }
34945     },
34946
34947     /**
34948      * Hides this menu and optionally all parent menus
34949      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34950      */
34951     hide : function(deep){
34952         if(this.el && this.isVisible()){
34953             this.fireEvent("beforehide", this);
34954             if(this.activeItem){
34955                 this.activeItem.deactivate();
34956                 this.activeItem = null;
34957             }
34958             this.el.hide();
34959             this.hidden = true;
34960             this.fireEvent("hide", this);
34961         }
34962         if(deep === true && this.parentMenu){
34963             this.parentMenu.hide(true);
34964         }
34965     },
34966
34967     /**
34968      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34969      * Any of the following are valid:
34970      * <ul>
34971      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34972      * <li>An HTMLElement object which will be converted to a menu item</li>
34973      * <li>A menu item config object that will be created as a new menu item</li>
34974      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34975      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34976      * </ul>
34977      * Usage:
34978      * <pre><code>
34979 // Create the menu
34980 var menu = new Roo.menu.Menu();
34981
34982 // Create a menu item to add by reference
34983 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34984
34985 // Add a bunch of items at once using different methods.
34986 // Only the last item added will be returned.
34987 var item = menu.add(
34988     menuItem,                // add existing item by ref
34989     'Dynamic Item',          // new TextItem
34990     '-',                     // new separator
34991     { text: 'Config Item' }  // new item by config
34992 );
34993 </code></pre>
34994      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34995      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34996      */
34997     add : function(){
34998         var a = arguments, l = a.length, item;
34999         for(var i = 0; i < l; i++){
35000             var el = a[i];
35001             if ((typeof(el) == "object") && el.xtype && el.xns) {
35002                 el = Roo.factory(el, Roo.menu);
35003             }
35004             
35005             if(el.render){ // some kind of Item
35006                 item = this.addItem(el);
35007             }else if(typeof el == "string"){ // string
35008                 if(el == "separator" || el == "-"){
35009                     item = this.addSeparator();
35010                 }else{
35011                     item = this.addText(el);
35012                 }
35013             }else if(el.tagName || el.el){ // element
35014                 item = this.addElement(el);
35015             }else if(typeof el == "object"){ // must be menu item config?
35016                 item = this.addMenuItem(el);
35017             }
35018         }
35019         return item;
35020     },
35021
35022     /**
35023      * Returns this menu's underlying {@link Roo.Element} object
35024      * @return {Roo.Element} The element
35025      */
35026     getEl : function(){
35027         if(!this.el){
35028             this.render();
35029         }
35030         return this.el;
35031     },
35032
35033     /**
35034      * Adds a separator bar to the menu
35035      * @return {Roo.menu.Item} The menu item that was added
35036      */
35037     addSeparator : function(){
35038         return this.addItem(new Roo.menu.Separator());
35039     },
35040
35041     /**
35042      * Adds an {@link Roo.Element} object to the menu
35043      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35044      * @return {Roo.menu.Item} The menu item that was added
35045      */
35046     addElement : function(el){
35047         return this.addItem(new Roo.menu.BaseItem(el));
35048     },
35049
35050     /**
35051      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35052      * @param {Roo.menu.Item} item The menu item to add
35053      * @return {Roo.menu.Item} The menu item that was added
35054      */
35055     addItem : function(item){
35056         this.items.add(item);
35057         if(this.ul){
35058             var li = document.createElement("li");
35059             li.className = "x-menu-list-item";
35060             this.ul.dom.appendChild(li);
35061             item.render(li, this);
35062             this.delayAutoWidth();
35063         }
35064         return item;
35065     },
35066
35067     /**
35068      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35069      * @param {Object} config A MenuItem config object
35070      * @return {Roo.menu.Item} The menu item that was added
35071      */
35072     addMenuItem : function(config){
35073         if(!(config instanceof Roo.menu.Item)){
35074             if(typeof config.checked == "boolean"){ // must be check menu item config?
35075                 config = new Roo.menu.CheckItem(config);
35076             }else{
35077                 config = new Roo.menu.Item(config);
35078             }
35079         }
35080         return this.addItem(config);
35081     },
35082
35083     /**
35084      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35085      * @param {String} text The text to display in the menu item
35086      * @return {Roo.menu.Item} The menu item that was added
35087      */
35088     addText : function(text){
35089         return this.addItem(new Roo.menu.TextItem({ text : text }));
35090     },
35091
35092     /**
35093      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35094      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35095      * @param {Roo.menu.Item} item The menu item to add
35096      * @return {Roo.menu.Item} The menu item that was added
35097      */
35098     insert : function(index, item){
35099         this.items.insert(index, item);
35100         if(this.ul){
35101             var li = document.createElement("li");
35102             li.className = "x-menu-list-item";
35103             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35104             item.render(li, this);
35105             this.delayAutoWidth();
35106         }
35107         return item;
35108     },
35109
35110     /**
35111      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35112      * @param {Roo.menu.Item} item The menu item to remove
35113      */
35114     remove : function(item){
35115         this.items.removeKey(item.id);
35116         item.destroy();
35117     },
35118
35119     /**
35120      * Removes and destroys all items in the menu
35121      */
35122     removeAll : function(){
35123         var f;
35124         while(f = this.items.first()){
35125             this.remove(f);
35126         }
35127     }
35128 });
35129
35130 // MenuNav is a private utility class used internally by the Menu
35131 Roo.menu.MenuNav = function(menu){
35132     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35133     this.scope = this.menu = menu;
35134 };
35135
35136 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35137     doRelay : function(e, h){
35138         var k = e.getKey();
35139         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35140             this.menu.tryActivate(0, 1);
35141             return false;
35142         }
35143         return h.call(this.scope || this, e, this.menu);
35144     },
35145
35146     up : function(e, m){
35147         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35148             m.tryActivate(m.items.length-1, -1);
35149         }
35150     },
35151
35152     down : function(e, m){
35153         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35154             m.tryActivate(0, 1);
35155         }
35156     },
35157
35158     right : function(e, m){
35159         if(m.activeItem){
35160             m.activeItem.expandMenu(true);
35161         }
35162     },
35163
35164     left : function(e, m){
35165         m.hide();
35166         if(m.parentMenu && m.parentMenu.activeItem){
35167             m.parentMenu.activeItem.activate();
35168         }
35169     },
35170
35171     enter : function(e, m){
35172         if(m.activeItem){
35173             e.stopPropagation();
35174             m.activeItem.onClick(e);
35175             m.fireEvent("click", this, m.activeItem);
35176             return true;
35177         }
35178     }
35179 });/*
35180  * Based on:
35181  * Ext JS Library 1.1.1
35182  * Copyright(c) 2006-2007, Ext JS, LLC.
35183  *
35184  * Originally Released Under LGPL - original licence link has changed is not relivant.
35185  *
35186  * Fork - LGPL
35187  * <script type="text/javascript">
35188  */
35189  
35190 /**
35191  * @class Roo.menu.MenuMgr
35192  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35193  * @singleton
35194  */
35195 Roo.menu.MenuMgr = function(){
35196    var menus, active, groups = {}, attached = false, lastShow = new Date();
35197
35198    // private - called when first menu is created
35199    function init(){
35200        menus = {};
35201        active = new Roo.util.MixedCollection();
35202        Roo.get(document).addKeyListener(27, function(){
35203            if(active.length > 0){
35204                hideAll();
35205            }
35206        });
35207    }
35208
35209    // private
35210    function hideAll(){
35211        if(active && active.length > 0){
35212            var c = active.clone();
35213            c.each(function(m){
35214                m.hide();
35215            });
35216        }
35217    }
35218
35219    // private
35220    function onHide(m){
35221        active.remove(m);
35222        if(active.length < 1){
35223            Roo.get(document).un("mousedown", onMouseDown);
35224            attached = false;
35225        }
35226    }
35227
35228    // private
35229    function onShow(m){
35230        var last = active.last();
35231        lastShow = new Date();
35232        active.add(m);
35233        if(!attached){
35234            Roo.get(document).on("mousedown", onMouseDown);
35235            attached = true;
35236        }
35237        if(m.parentMenu){
35238           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35239           m.parentMenu.activeChild = m;
35240        }else if(last && last.isVisible()){
35241           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35242        }
35243    }
35244
35245    // private
35246    function onBeforeHide(m){
35247        if(m.activeChild){
35248            m.activeChild.hide();
35249        }
35250        if(m.autoHideTimer){
35251            clearTimeout(m.autoHideTimer);
35252            delete m.autoHideTimer;
35253        }
35254    }
35255
35256    // private
35257    function onBeforeShow(m){
35258        var pm = m.parentMenu;
35259        if(!pm && !m.allowOtherMenus){
35260            hideAll();
35261        }else if(pm && pm.activeChild && active != m){
35262            pm.activeChild.hide();
35263        }
35264    }
35265
35266    // private
35267    function onMouseDown(e){
35268        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35269            hideAll();
35270        }
35271    }
35272
35273    // private
35274    function onBeforeCheck(mi, state){
35275        if(state){
35276            var g = groups[mi.group];
35277            for(var i = 0, l = g.length; i < l; i++){
35278                if(g[i] != mi){
35279                    g[i].setChecked(false);
35280                }
35281            }
35282        }
35283    }
35284
35285    return {
35286
35287        /**
35288         * Hides all menus that are currently visible
35289         */
35290        hideAll : function(){
35291             hideAll();  
35292        },
35293
35294        // private
35295        register : function(menu){
35296            if(!menus){
35297                init();
35298            }
35299            menus[menu.id] = menu;
35300            menu.on("beforehide", onBeforeHide);
35301            menu.on("hide", onHide);
35302            menu.on("beforeshow", onBeforeShow);
35303            menu.on("show", onShow);
35304            var g = menu.group;
35305            if(g && menu.events["checkchange"]){
35306                if(!groups[g]){
35307                    groups[g] = [];
35308                }
35309                groups[g].push(menu);
35310                menu.on("checkchange", onCheck);
35311            }
35312        },
35313
35314         /**
35315          * Returns a {@link Roo.menu.Menu} object
35316          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
35317          * be used to generate and return a new Menu instance.
35318          */
35319        get : function(menu){
35320            if(typeof menu == "string"){ // menu id
35321                return menus[menu];
35322            }else if(menu.events){  // menu instance
35323                return menu;
35324            }else if(typeof menu.length == 'number'){ // array of menu items?
35325                return new Roo.menu.Menu({items:menu});
35326            }else{ // otherwise, must be a config
35327                return new Roo.menu.Menu(menu);
35328            }
35329        },
35330
35331        // private
35332        unregister : function(menu){
35333            delete menus[menu.id];
35334            menu.un("beforehide", onBeforeHide);
35335            menu.un("hide", onHide);
35336            menu.un("beforeshow", onBeforeShow);
35337            menu.un("show", onShow);
35338            var g = menu.group;
35339            if(g && menu.events["checkchange"]){
35340                groups[g].remove(menu);
35341                menu.un("checkchange", onCheck);
35342            }
35343        },
35344
35345        // private
35346        registerCheckable : function(menuItem){
35347            var g = menuItem.group;
35348            if(g){
35349                if(!groups[g]){
35350                    groups[g] = [];
35351                }
35352                groups[g].push(menuItem);
35353                menuItem.on("beforecheckchange", onBeforeCheck);
35354            }
35355        },
35356
35357        // private
35358        unregisterCheckable : function(menuItem){
35359            var g = menuItem.group;
35360            if(g){
35361                groups[g].remove(menuItem);
35362                menuItem.un("beforecheckchange", onBeforeCheck);
35363            }
35364        }
35365    };
35366 }();/*
35367  * Based on:
35368  * Ext JS Library 1.1.1
35369  * Copyright(c) 2006-2007, Ext JS, LLC.
35370  *
35371  * Originally Released Under LGPL - original licence link has changed is not relivant.
35372  *
35373  * Fork - LGPL
35374  * <script type="text/javascript">
35375  */
35376  
35377
35378 /**
35379  * @class Roo.menu.BaseItem
35380  * @extends Roo.Component
35381  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
35382  * management and base configuration options shared by all menu components.
35383  * @constructor
35384  * Creates a new BaseItem
35385  * @param {Object} config Configuration options
35386  */
35387 Roo.menu.BaseItem = function(config){
35388     Roo.menu.BaseItem.superclass.constructor.call(this, config);
35389
35390     this.addEvents({
35391         /**
35392          * @event click
35393          * Fires when this item is clicked
35394          * @param {Roo.menu.BaseItem} this
35395          * @param {Roo.EventObject} e
35396          */
35397         click: true,
35398         /**
35399          * @event activate
35400          * Fires when this item is activated
35401          * @param {Roo.menu.BaseItem} this
35402          */
35403         activate : true,
35404         /**
35405          * @event deactivate
35406          * Fires when this item is deactivated
35407          * @param {Roo.menu.BaseItem} this
35408          */
35409         deactivate : true
35410     });
35411
35412     if(this.handler){
35413         this.on("click", this.handler, this.scope, true);
35414     }
35415 };
35416
35417 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
35418     /**
35419      * @cfg {Function} handler
35420      * A function that will handle the click event of this menu item (defaults to undefined)
35421      */
35422     /**
35423      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
35424      */
35425     canActivate : false,
35426     /**
35427      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
35428      */
35429     activeClass : "x-menu-item-active",
35430     /**
35431      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
35432      */
35433     hideOnClick : true,
35434     /**
35435      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
35436      */
35437     hideDelay : 100,
35438
35439     // private
35440     ctype: "Roo.menu.BaseItem",
35441
35442     // private
35443     actionMode : "container",
35444
35445     // private
35446     render : function(container, parentMenu){
35447         this.parentMenu = parentMenu;
35448         Roo.menu.BaseItem.superclass.render.call(this, container);
35449         this.container.menuItemId = this.id;
35450     },
35451
35452     // private
35453     onRender : function(container, position){
35454         this.el = Roo.get(this.el);
35455         container.dom.appendChild(this.el.dom);
35456     },
35457
35458     // private
35459     onClick : function(e){
35460         if(!this.disabled && this.fireEvent("click", this, e) !== false
35461                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
35462             this.handleClick(e);
35463         }else{
35464             e.stopEvent();
35465         }
35466     },
35467
35468     // private
35469     activate : function(){
35470         if(this.disabled){
35471             return false;
35472         }
35473         var li = this.container;
35474         li.addClass(this.activeClass);
35475         this.region = li.getRegion().adjust(2, 2, -2, -2);
35476         this.fireEvent("activate", this);
35477         return true;
35478     },
35479
35480     // private
35481     deactivate : function(){
35482         this.container.removeClass(this.activeClass);
35483         this.fireEvent("deactivate", this);
35484     },
35485
35486     // private
35487     shouldDeactivate : function(e){
35488         return !this.region || !this.region.contains(e.getPoint());
35489     },
35490
35491     // private
35492     handleClick : function(e){
35493         if(this.hideOnClick){
35494             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
35495         }
35496     },
35497
35498     // private
35499     expandMenu : function(autoActivate){
35500         // do nothing
35501     },
35502
35503     // private
35504     hideMenu : function(){
35505         // do nothing
35506     }
35507 });/*
35508  * Based on:
35509  * Ext JS Library 1.1.1
35510  * Copyright(c) 2006-2007, Ext JS, LLC.
35511  *
35512  * Originally Released Under LGPL - original licence link has changed is not relivant.
35513  *
35514  * Fork - LGPL
35515  * <script type="text/javascript">
35516  */
35517  
35518 /**
35519  * @class Roo.menu.Adapter
35520  * @extends Roo.menu.BaseItem
35521  * 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.
35522  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
35523  * @constructor
35524  * Creates a new Adapter
35525  * @param {Object} config Configuration options
35526  */
35527 Roo.menu.Adapter = function(component, config){
35528     Roo.menu.Adapter.superclass.constructor.call(this, config);
35529     this.component = component;
35530 };
35531 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
35532     // private
35533     canActivate : true,
35534
35535     // private
35536     onRender : function(container, position){
35537         this.component.render(container);
35538         this.el = this.component.getEl();
35539     },
35540
35541     // private
35542     activate : function(){
35543         if(this.disabled){
35544             return false;
35545         }
35546         this.component.focus();
35547         this.fireEvent("activate", this);
35548         return true;
35549     },
35550
35551     // private
35552     deactivate : function(){
35553         this.fireEvent("deactivate", this);
35554     },
35555
35556     // private
35557     disable : function(){
35558         this.component.disable();
35559         Roo.menu.Adapter.superclass.disable.call(this);
35560     },
35561
35562     // private
35563     enable : function(){
35564         this.component.enable();
35565         Roo.menu.Adapter.superclass.enable.call(this);
35566     }
35567 });/*
35568  * Based on:
35569  * Ext JS Library 1.1.1
35570  * Copyright(c) 2006-2007, Ext JS, LLC.
35571  *
35572  * Originally Released Under LGPL - original licence link has changed is not relivant.
35573  *
35574  * Fork - LGPL
35575  * <script type="text/javascript">
35576  */
35577
35578 /**
35579  * @class Roo.menu.TextItem
35580  * @extends Roo.menu.BaseItem
35581  * Adds a static text string to a menu, usually used as either a heading or group separator.
35582  * Note: old style constructor with text is still supported.
35583  * 
35584  * @constructor
35585  * Creates a new TextItem
35586  * @param {Object} cfg Configuration
35587  */
35588 Roo.menu.TextItem = function(cfg){
35589     if (typeof(cfg) == 'string') {
35590         this.text = cfg;
35591     } else {
35592         Roo.apply(this,cfg);
35593     }
35594     
35595     Roo.menu.TextItem.superclass.constructor.call(this);
35596 };
35597
35598 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35599     /**
35600      * @cfg {Boolean} text Text to show on item.
35601      */
35602     text : '',
35603     
35604     /**
35605      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35606      */
35607     hideOnClick : false,
35608     /**
35609      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35610      */
35611     itemCls : "x-menu-text",
35612
35613     // private
35614     onRender : function(){
35615         var s = document.createElement("span");
35616         s.className = this.itemCls;
35617         s.innerHTML = this.text;
35618         this.el = s;
35619         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35620     }
35621 });/*
35622  * Based on:
35623  * Ext JS Library 1.1.1
35624  * Copyright(c) 2006-2007, Ext JS, LLC.
35625  *
35626  * Originally Released Under LGPL - original licence link has changed is not relivant.
35627  *
35628  * Fork - LGPL
35629  * <script type="text/javascript">
35630  */
35631
35632 /**
35633  * @class Roo.menu.Separator
35634  * @extends Roo.menu.BaseItem
35635  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35636  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35637  * @constructor
35638  * @param {Object} config Configuration options
35639  */
35640 Roo.menu.Separator = function(config){
35641     Roo.menu.Separator.superclass.constructor.call(this, config);
35642 };
35643
35644 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35645     /**
35646      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35647      */
35648     itemCls : "x-menu-sep",
35649     /**
35650      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35651      */
35652     hideOnClick : false,
35653
35654     // private
35655     onRender : function(li){
35656         var s = document.createElement("span");
35657         s.className = this.itemCls;
35658         s.innerHTML = "&#160;";
35659         this.el = s;
35660         li.addClass("x-menu-sep-li");
35661         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35662     }
35663 });/*
35664  * Based on:
35665  * Ext JS Library 1.1.1
35666  * Copyright(c) 2006-2007, Ext JS, LLC.
35667  *
35668  * Originally Released Under LGPL - original licence link has changed is not relivant.
35669  *
35670  * Fork - LGPL
35671  * <script type="text/javascript">
35672  */
35673 /**
35674  * @class Roo.menu.Item
35675  * @extends Roo.menu.BaseItem
35676  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35677  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35678  * activation and click handling.
35679  * @constructor
35680  * Creates a new Item
35681  * @param {Object} config Configuration options
35682  */
35683 Roo.menu.Item = function(config){
35684     Roo.menu.Item.superclass.constructor.call(this, config);
35685     if(this.menu){
35686         this.menu = Roo.menu.MenuMgr.get(this.menu);
35687     }
35688 };
35689 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35690     
35691     /**
35692      * @cfg {String} text
35693      * The text to show on the menu item.
35694      */
35695     text: '',
35696      /**
35697      * @cfg {String} HTML to render in menu
35698      * The text to show on the menu item (HTML version).
35699      */
35700     html: '',
35701     /**
35702      * @cfg {String} icon
35703      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35704      */
35705     icon: undefined,
35706     /**
35707      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35708      */
35709     itemCls : "x-menu-item",
35710     /**
35711      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35712      */
35713     canActivate : true,
35714     /**
35715      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35716      */
35717     showDelay: 200,
35718     // doc'd in BaseItem
35719     hideDelay: 200,
35720
35721     // private
35722     ctype: "Roo.menu.Item",
35723     
35724     // private
35725     onRender : function(container, position){
35726         var el = document.createElement("a");
35727         el.hideFocus = true;
35728         el.unselectable = "on";
35729         el.href = this.href || "#";
35730         if(this.hrefTarget){
35731             el.target = this.hrefTarget;
35732         }
35733         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35734         
35735         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35736         
35737         el.innerHTML = String.format(
35738                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35739                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35740         this.el = el;
35741         Roo.menu.Item.superclass.onRender.call(this, container, position);
35742     },
35743
35744     /**
35745      * Sets the text to display in this menu item
35746      * @param {String} text The text to display
35747      * @param {Boolean} isHTML true to indicate text is pure html.
35748      */
35749     setText : function(text, isHTML){
35750         if (isHTML) {
35751             this.html = text;
35752         } else {
35753             this.text = text;
35754             this.html = '';
35755         }
35756         if(this.rendered){
35757             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35758      
35759             this.el.update(String.format(
35760                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35761                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35762             this.parentMenu.autoWidth();
35763         }
35764     },
35765
35766     // private
35767     handleClick : function(e){
35768         if(!this.href){ // if no link defined, stop the event automatically
35769             e.stopEvent();
35770         }
35771         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35772     },
35773
35774     // private
35775     activate : function(autoExpand){
35776         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35777             this.focus();
35778             if(autoExpand){
35779                 this.expandMenu();
35780             }
35781         }
35782         return true;
35783     },
35784
35785     // private
35786     shouldDeactivate : function(e){
35787         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35788             if(this.menu && this.menu.isVisible()){
35789                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35790             }
35791             return true;
35792         }
35793         return false;
35794     },
35795
35796     // private
35797     deactivate : function(){
35798         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35799         this.hideMenu();
35800     },
35801
35802     // private
35803     expandMenu : function(autoActivate){
35804         if(!this.disabled && this.menu){
35805             clearTimeout(this.hideTimer);
35806             delete this.hideTimer;
35807             if(!this.menu.isVisible() && !this.showTimer){
35808                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35809             }else if (this.menu.isVisible() && autoActivate){
35810                 this.menu.tryActivate(0, 1);
35811             }
35812         }
35813     },
35814
35815     // private
35816     deferExpand : function(autoActivate){
35817         delete this.showTimer;
35818         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35819         if(autoActivate){
35820             this.menu.tryActivate(0, 1);
35821         }
35822     },
35823
35824     // private
35825     hideMenu : function(){
35826         clearTimeout(this.showTimer);
35827         delete this.showTimer;
35828         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35829             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35830         }
35831     },
35832
35833     // private
35834     deferHide : function(){
35835         delete this.hideTimer;
35836         this.menu.hide();
35837     }
35838 });/*
35839  * Based on:
35840  * Ext JS Library 1.1.1
35841  * Copyright(c) 2006-2007, Ext JS, LLC.
35842  *
35843  * Originally Released Under LGPL - original licence link has changed is not relivant.
35844  *
35845  * Fork - LGPL
35846  * <script type="text/javascript">
35847  */
35848  
35849 /**
35850  * @class Roo.menu.CheckItem
35851  * @extends Roo.menu.Item
35852  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35853  * @constructor
35854  * Creates a new CheckItem
35855  * @param {Object} config Configuration options
35856  */
35857 Roo.menu.CheckItem = function(config){
35858     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35859     this.addEvents({
35860         /**
35861          * @event beforecheckchange
35862          * Fires before the checked value is set, providing an opportunity to cancel if needed
35863          * @param {Roo.menu.CheckItem} this
35864          * @param {Boolean} checked The new checked value that will be set
35865          */
35866         "beforecheckchange" : true,
35867         /**
35868          * @event checkchange
35869          * Fires after the checked value has been set
35870          * @param {Roo.menu.CheckItem} this
35871          * @param {Boolean} checked The checked value that was set
35872          */
35873         "checkchange" : true
35874     });
35875     if(this.checkHandler){
35876         this.on('checkchange', this.checkHandler, this.scope);
35877     }
35878 };
35879 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35880     /**
35881      * @cfg {String} group
35882      * All check items with the same group name will automatically be grouped into a single-select
35883      * radio button group (defaults to '')
35884      */
35885     /**
35886      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35887      */
35888     itemCls : "x-menu-item x-menu-check-item",
35889     /**
35890      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35891      */
35892     groupClass : "x-menu-group-item",
35893
35894     /**
35895      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35896      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35897      * initialized with checked = true will be rendered as checked.
35898      */
35899     checked: false,
35900
35901     // private
35902     ctype: "Roo.menu.CheckItem",
35903
35904     // private
35905     onRender : function(c){
35906         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35907         if(this.group){
35908             this.el.addClass(this.groupClass);
35909         }
35910         Roo.menu.MenuMgr.registerCheckable(this);
35911         if(this.checked){
35912             this.checked = false;
35913             this.setChecked(true, true);
35914         }
35915     },
35916
35917     // private
35918     destroy : function(){
35919         if(this.rendered){
35920             Roo.menu.MenuMgr.unregisterCheckable(this);
35921         }
35922         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35923     },
35924
35925     /**
35926      * Set the checked state of this item
35927      * @param {Boolean} checked The new checked value
35928      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35929      */
35930     setChecked : function(state, suppressEvent){
35931         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35932             if(this.container){
35933                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35934             }
35935             this.checked = state;
35936             if(suppressEvent !== true){
35937                 this.fireEvent("checkchange", this, state);
35938             }
35939         }
35940     },
35941
35942     // private
35943     handleClick : function(e){
35944        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35945            this.setChecked(!this.checked);
35946        }
35947        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35948     }
35949 });/*
35950  * Based on:
35951  * Ext JS Library 1.1.1
35952  * Copyright(c) 2006-2007, Ext JS, LLC.
35953  *
35954  * Originally Released Under LGPL - original licence link has changed is not relivant.
35955  *
35956  * Fork - LGPL
35957  * <script type="text/javascript">
35958  */
35959  
35960 /**
35961  * @class Roo.menu.DateItem
35962  * @extends Roo.menu.Adapter
35963  * A menu item that wraps the {@link Roo.DatPicker} component.
35964  * @constructor
35965  * Creates a new DateItem
35966  * @param {Object} config Configuration options
35967  */
35968 Roo.menu.DateItem = function(config){
35969     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35970     /** The Roo.DatePicker object @type Roo.DatePicker */
35971     this.picker = this.component;
35972     this.addEvents({select: true});
35973     
35974     this.picker.on("render", function(picker){
35975         picker.getEl().swallowEvent("click");
35976         picker.container.addClass("x-menu-date-item");
35977     });
35978
35979     this.picker.on("select", this.onSelect, this);
35980 };
35981
35982 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35983     // private
35984     onSelect : function(picker, date){
35985         this.fireEvent("select", this, date, picker);
35986         Roo.menu.DateItem.superclass.handleClick.call(this);
35987     }
35988 });/*
35989  * Based on:
35990  * Ext JS Library 1.1.1
35991  * Copyright(c) 2006-2007, Ext JS, LLC.
35992  *
35993  * Originally Released Under LGPL - original licence link has changed is not relivant.
35994  *
35995  * Fork - LGPL
35996  * <script type="text/javascript">
35997  */
35998  
35999 /**
36000  * @class Roo.menu.ColorItem
36001  * @extends Roo.menu.Adapter
36002  * A menu item that wraps the {@link Roo.ColorPalette} component.
36003  * @constructor
36004  * Creates a new ColorItem
36005  * @param {Object} config Configuration options
36006  */
36007 Roo.menu.ColorItem = function(config){
36008     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36009     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36010     this.palette = this.component;
36011     this.relayEvents(this.palette, ["select"]);
36012     if(this.selectHandler){
36013         this.on('select', this.selectHandler, this.scope);
36014     }
36015 };
36016 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36017  * Based on:
36018  * Ext JS Library 1.1.1
36019  * Copyright(c) 2006-2007, Ext JS, LLC.
36020  *
36021  * Originally Released Under LGPL - original licence link has changed is not relivant.
36022  *
36023  * Fork - LGPL
36024  * <script type="text/javascript">
36025  */
36026  
36027
36028 /**
36029  * @class Roo.menu.DateMenu
36030  * @extends Roo.menu.Menu
36031  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36032  * @constructor
36033  * Creates a new DateMenu
36034  * @param {Object} config Configuration options
36035  */
36036 Roo.menu.DateMenu = function(config){
36037     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36038     this.plain = true;
36039     var di = new Roo.menu.DateItem(config);
36040     this.add(di);
36041     /**
36042      * The {@link Roo.DatePicker} instance for this DateMenu
36043      * @type DatePicker
36044      */
36045     this.picker = di.picker;
36046     /**
36047      * @event select
36048      * @param {DatePicker} picker
36049      * @param {Date} date
36050      */
36051     this.relayEvents(di, ["select"]);
36052
36053     this.on('beforeshow', function(){
36054         if(this.picker){
36055             this.picker.hideMonthPicker(true);
36056         }
36057     }, this);
36058 };
36059 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36060     cls:'x-date-menu'
36061 });/*
36062  * Based on:
36063  * Ext JS Library 1.1.1
36064  * Copyright(c) 2006-2007, Ext JS, LLC.
36065  *
36066  * Originally Released Under LGPL - original licence link has changed is not relivant.
36067  *
36068  * Fork - LGPL
36069  * <script type="text/javascript">
36070  */
36071  
36072
36073 /**
36074  * @class Roo.menu.ColorMenu
36075  * @extends Roo.menu.Menu
36076  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36077  * @constructor
36078  * Creates a new ColorMenu
36079  * @param {Object} config Configuration options
36080  */
36081 Roo.menu.ColorMenu = function(config){
36082     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36083     this.plain = true;
36084     var ci = new Roo.menu.ColorItem(config);
36085     this.add(ci);
36086     /**
36087      * The {@link Roo.ColorPalette} instance for this ColorMenu
36088      * @type ColorPalette
36089      */
36090     this.palette = ci.palette;
36091     /**
36092      * @event select
36093      * @param {ColorPalette} palette
36094      * @param {String} color
36095      */
36096     this.relayEvents(ci, ["select"]);
36097 };
36098 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
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.Field
36111  * @extends Roo.BoxComponent
36112  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36113  * @constructor
36114  * Creates a new Field
36115  * @param {Object} config Configuration options
36116  */
36117 Roo.form.Field = function(config){
36118     Roo.form.Field.superclass.constructor.call(this, config);
36119 };
36120
36121 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36122     /**
36123      * @cfg {String} fieldLabel Label to use when rendering a form.
36124      */
36125        /**
36126      * @cfg {String} qtip Mouse over tip
36127      */
36128      
36129     /**
36130      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36131      */
36132     invalidClass : "x-form-invalid",
36133     /**
36134      * @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")
36135      */
36136     invalidText : "The value in this field is invalid",
36137     /**
36138      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36139      */
36140     focusClass : "x-form-focus",
36141     /**
36142      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36143       automatic validation (defaults to "keyup").
36144      */
36145     validationEvent : "keyup",
36146     /**
36147      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36148      */
36149     validateOnBlur : true,
36150     /**
36151      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36152      */
36153     validationDelay : 250,
36154     /**
36155      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36156      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36157      */
36158     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36159     /**
36160      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36161      */
36162     fieldClass : "x-form-field",
36163     /**
36164      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36165      *<pre>
36166 Value         Description
36167 -----------   ----------------------------------------------------------------------
36168 qtip          Display a quick tip when the user hovers over the field
36169 title         Display a default browser title attribute popup
36170 under         Add a block div beneath the field containing the error text
36171 side          Add an error icon to the right of the field with a popup on hover
36172 [element id]  Add the error text directly to the innerHTML of the specified element
36173 </pre>
36174      */
36175     msgTarget : 'qtip',
36176     /**
36177      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36178      */
36179     msgFx : 'normal',
36180
36181     /**
36182      * @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.
36183      */
36184     readOnly : false,
36185
36186     /**
36187      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36188      */
36189     disabled : false,
36190
36191     /**
36192      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36193      */
36194     inputType : undefined,
36195     
36196     /**
36197      * @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).
36198          */
36199         tabIndex : undefined,
36200         
36201     // private
36202     isFormField : true,
36203
36204     // private
36205     hasFocus : false,
36206     /**
36207      * @property {Roo.Element} fieldEl
36208      * Element Containing the rendered Field (with label etc.)
36209      */
36210     /**
36211      * @cfg {Mixed} value A value to initialize this field with.
36212      */
36213     value : undefined,
36214
36215     /**
36216      * @cfg {String} name The field's HTML name attribute.
36217      */
36218     /**
36219      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36220      */
36221
36222         // private ??
36223         initComponent : function(){
36224         Roo.form.Field.superclass.initComponent.call(this);
36225         this.addEvents({
36226             /**
36227              * @event focus
36228              * Fires when this field receives input focus.
36229              * @param {Roo.form.Field} this
36230              */
36231             focus : true,
36232             /**
36233              * @event blur
36234              * Fires when this field loses input focus.
36235              * @param {Roo.form.Field} this
36236              */
36237             blur : true,
36238             /**
36239              * @event specialkey
36240              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36241              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36242              * @param {Roo.form.Field} this
36243              * @param {Roo.EventObject} e The event object
36244              */
36245             specialkey : true,
36246             /**
36247              * @event change
36248              * Fires just before the field blurs if the field value has changed.
36249              * @param {Roo.form.Field} this
36250              * @param {Mixed} newValue The new value
36251              * @param {Mixed} oldValue The original value
36252              */
36253             change : true,
36254             /**
36255              * @event invalid
36256              * Fires after the field has been marked as invalid.
36257              * @param {Roo.form.Field} this
36258              * @param {String} msg The validation message
36259              */
36260             invalid : true,
36261             /**
36262              * @event valid
36263              * Fires after the field has been validated with no errors.
36264              * @param {Roo.form.Field} this
36265              */
36266             valid : true,
36267              /**
36268              * @event keyup
36269              * Fires after the key up
36270              * @param {Roo.form.Field} this
36271              * @param {Roo.EventObject}  e The event Object
36272              */
36273             keyup : true
36274         });
36275     },
36276
36277     /**
36278      * Returns the name attribute of the field if available
36279      * @return {String} name The field name
36280      */
36281     getName: function(){
36282          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36283     },
36284
36285     // private
36286     onRender : function(ct, position){
36287         Roo.form.Field.superclass.onRender.call(this, ct, position);
36288         if(!this.el){
36289             var cfg = this.getAutoCreate();
36290             if(!cfg.name){
36291                 cfg.name = this.name || this.id;
36292             }
36293             if(this.inputType){
36294                 cfg.type = this.inputType;
36295             }
36296             this.el = ct.createChild(cfg, position);
36297         }
36298         var type = this.el.dom.type;
36299         if(type){
36300             if(type == 'password'){
36301                 type = 'text';
36302             }
36303             this.el.addClass('x-form-'+type);
36304         }
36305         if(this.readOnly){
36306             this.el.dom.readOnly = true;
36307         }
36308         if(this.tabIndex !== undefined){
36309             this.el.dom.setAttribute('tabIndex', this.tabIndex);
36310         }
36311
36312         this.el.addClass([this.fieldClass, this.cls]);
36313         this.initValue();
36314     },
36315
36316     /**
36317      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
36318      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
36319      * @return {Roo.form.Field} this
36320      */
36321     applyTo : function(target){
36322         this.allowDomMove = false;
36323         this.el = Roo.get(target);
36324         this.render(this.el.dom.parentNode);
36325         return this;
36326     },
36327
36328     // private
36329     initValue : function(){
36330         if(this.value !== undefined){
36331             this.setValue(this.value);
36332         }else if(this.el.dom.value.length > 0){
36333             this.setValue(this.el.dom.value);
36334         }
36335     },
36336
36337     /**
36338      * Returns true if this field has been changed since it was originally loaded and is not disabled.
36339      */
36340     isDirty : function() {
36341         if(this.disabled) {
36342             return false;
36343         }
36344         return String(this.getValue()) !== String(this.originalValue);
36345     },
36346
36347     // private
36348     afterRender : function(){
36349         Roo.form.Field.superclass.afterRender.call(this);
36350         this.initEvents();
36351     },
36352
36353     // private
36354     fireKey : function(e){
36355         //Roo.log('field ' + e.getKey());
36356         if(e.isNavKeyPress()){
36357             this.fireEvent("specialkey", this, e);
36358         }
36359     },
36360
36361     /**
36362      * Resets the current field value to the originally loaded value and clears any validation messages
36363      */
36364     reset : function(){
36365         this.setValue(this.originalValue);
36366         this.clearInvalid();
36367     },
36368
36369     // private
36370     initEvents : function(){
36371         // safari killled keypress - so keydown is now used..
36372         this.el.on("keydown" , this.fireKey,  this);
36373         this.el.on("focus", this.onFocus,  this);
36374         this.el.on("blur", this.onBlur,  this);
36375         this.el.relayEvent('keyup', this);
36376
36377         // reference to original value for reset
36378         this.originalValue = this.getValue();
36379     },
36380
36381     // private
36382     onFocus : function(){
36383         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36384             this.el.addClass(this.focusClass);
36385         }
36386         if(!this.hasFocus){
36387             this.hasFocus = true;
36388             this.startValue = this.getValue();
36389             this.fireEvent("focus", this);
36390         }
36391     },
36392
36393     beforeBlur : Roo.emptyFn,
36394
36395     // private
36396     onBlur : function(){
36397         this.beforeBlur();
36398         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36399             this.el.removeClass(this.focusClass);
36400         }
36401         this.hasFocus = false;
36402         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
36403             this.validate();
36404         }
36405         var v = this.getValue();
36406         if(String(v) !== String(this.startValue)){
36407             this.fireEvent('change', this, v, this.startValue);
36408         }
36409         this.fireEvent("blur", this);
36410     },
36411
36412     /**
36413      * Returns whether or not the field value is currently valid
36414      * @param {Boolean} preventMark True to disable marking the field invalid
36415      * @return {Boolean} True if the value is valid, else false
36416      */
36417     isValid : function(preventMark){
36418         if(this.disabled){
36419             return true;
36420         }
36421         var restore = this.preventMark;
36422         this.preventMark = preventMark === true;
36423         var v = this.validateValue(this.processValue(this.getRawValue()));
36424         this.preventMark = restore;
36425         return v;
36426     },
36427
36428     /**
36429      * Validates the field value
36430      * @return {Boolean} True if the value is valid, else false
36431      */
36432     validate : function(){
36433         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
36434             this.clearInvalid();
36435             return true;
36436         }
36437         return false;
36438     },
36439
36440     processValue : function(value){
36441         return value;
36442     },
36443
36444     // private
36445     // Subclasses should provide the validation implementation by overriding this
36446     validateValue : function(value){
36447         return true;
36448     },
36449
36450     /**
36451      * Mark this field as invalid
36452      * @param {String} msg The validation message
36453      */
36454     markInvalid : function(msg){
36455         if(!this.rendered || this.preventMark){ // not rendered
36456             return;
36457         }
36458         this.el.addClass(this.invalidClass);
36459         msg = msg || this.invalidText;
36460         switch(this.msgTarget){
36461             case 'qtip':
36462                 this.el.dom.qtip = msg;
36463                 this.el.dom.qclass = 'x-form-invalid-tip';
36464                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
36465                     Roo.QuickTips.enable();
36466                 }
36467                 break;
36468             case 'title':
36469                 this.el.dom.title = msg;
36470                 break;
36471             case 'under':
36472                 if(!this.errorEl){
36473                     var elp = this.el.findParent('.x-form-element', 5, true);
36474                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
36475                     this.errorEl.setWidth(elp.getWidth(true)-20);
36476                 }
36477                 this.errorEl.update(msg);
36478                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
36479                 break;
36480             case 'side':
36481                 if(!this.errorIcon){
36482                     var elp = this.el.findParent('.x-form-element', 5, true);
36483                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
36484                 }
36485                 this.alignErrorIcon();
36486                 this.errorIcon.dom.qtip = msg;
36487                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
36488                 this.errorIcon.show();
36489                 this.on('resize', this.alignErrorIcon, this);
36490                 break;
36491             default:
36492                 var t = Roo.getDom(this.msgTarget);
36493                 t.innerHTML = msg;
36494                 t.style.display = this.msgDisplay;
36495                 break;
36496         }
36497         this.fireEvent('invalid', this, msg);
36498     },
36499
36500     // private
36501     alignErrorIcon : function(){
36502         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
36503     },
36504
36505     /**
36506      * Clear any invalid styles/messages for this field
36507      */
36508     clearInvalid : function(){
36509         if(!this.rendered || this.preventMark){ // not rendered
36510             return;
36511         }
36512         this.el.removeClass(this.invalidClass);
36513         switch(this.msgTarget){
36514             case 'qtip':
36515                 this.el.dom.qtip = '';
36516                 break;
36517             case 'title':
36518                 this.el.dom.title = '';
36519                 break;
36520             case 'under':
36521                 if(this.errorEl){
36522                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
36523                 }
36524                 break;
36525             case 'side':
36526                 if(this.errorIcon){
36527                     this.errorIcon.dom.qtip = '';
36528                     this.errorIcon.hide();
36529                     this.un('resize', this.alignErrorIcon, this);
36530                 }
36531                 break;
36532             default:
36533                 var t = Roo.getDom(this.msgTarget);
36534                 t.innerHTML = '';
36535                 t.style.display = 'none';
36536                 break;
36537         }
36538         this.fireEvent('valid', this);
36539     },
36540
36541     /**
36542      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
36543      * @return {Mixed} value The field value
36544      */
36545     getRawValue : function(){
36546         var v = this.el.getValue();
36547         if(v === this.emptyText){
36548             v = '';
36549         }
36550         return v;
36551     },
36552
36553     /**
36554      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
36555      * @return {Mixed} value The field value
36556      */
36557     getValue : function(){
36558         var v = this.el.getValue();
36559         if(v === this.emptyText || v === undefined){
36560             v = '';
36561         }
36562         return v;
36563     },
36564
36565     /**
36566      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
36567      * @param {Mixed} value The value to set
36568      */
36569     setRawValue : function(v){
36570         return this.el.dom.value = (v === null || v === undefined ? '' : v);
36571     },
36572
36573     /**
36574      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
36575      * @param {Mixed} value The value to set
36576      */
36577     setValue : function(v){
36578         this.value = v;
36579         if(this.rendered){
36580             this.el.dom.value = (v === null || v === undefined ? '' : v);
36581              this.validate();
36582         }
36583     },
36584
36585     adjustSize : function(w, h){
36586         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
36587         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
36588         return s;
36589     },
36590
36591     adjustWidth : function(tag, w){
36592         tag = tag.toLowerCase();
36593         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36594             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36595                 if(tag == 'input'){
36596                     return w + 2;
36597                 }
36598                 if(tag = 'textarea'){
36599                     return w-2;
36600                 }
36601             }else if(Roo.isOpera){
36602                 if(tag == 'input'){
36603                     return w + 2;
36604                 }
36605                 if(tag = 'textarea'){
36606                     return w-2;
36607                 }
36608             }
36609         }
36610         return w;
36611     }
36612 });
36613
36614
36615 // anything other than normal should be considered experimental
36616 Roo.form.Field.msgFx = {
36617     normal : {
36618         show: function(msgEl, f){
36619             msgEl.setDisplayed('block');
36620         },
36621
36622         hide : function(msgEl, f){
36623             msgEl.setDisplayed(false).update('');
36624         }
36625     },
36626
36627     slide : {
36628         show: function(msgEl, f){
36629             msgEl.slideIn('t', {stopFx:true});
36630         },
36631
36632         hide : function(msgEl, f){
36633             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36634         }
36635     },
36636
36637     slideRight : {
36638         show: function(msgEl, f){
36639             msgEl.fixDisplay();
36640             msgEl.alignTo(f.el, 'tl-tr');
36641             msgEl.slideIn('l', {stopFx:true});
36642         },
36643
36644         hide : function(msgEl, f){
36645             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36646         }
36647     }
36648 };/*
36649  * Based on:
36650  * Ext JS Library 1.1.1
36651  * Copyright(c) 2006-2007, Ext JS, LLC.
36652  *
36653  * Originally Released Under LGPL - original licence link has changed is not relivant.
36654  *
36655  * Fork - LGPL
36656  * <script type="text/javascript">
36657  */
36658  
36659
36660 /**
36661  * @class Roo.form.TextField
36662  * @extends Roo.form.Field
36663  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36664  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36665  * @constructor
36666  * Creates a new TextField
36667  * @param {Object} config Configuration options
36668  */
36669 Roo.form.TextField = function(config){
36670     Roo.form.TextField.superclass.constructor.call(this, config);
36671     this.addEvents({
36672         /**
36673          * @event autosize
36674          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36675          * according to the default logic, but this event provides a hook for the developer to apply additional
36676          * logic at runtime to resize the field if needed.
36677              * @param {Roo.form.Field} this This text field
36678              * @param {Number} width The new field width
36679              */
36680         autosize : true
36681     });
36682 };
36683
36684 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36685     /**
36686      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36687      */
36688     grow : false,
36689     /**
36690      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36691      */
36692     growMin : 30,
36693     /**
36694      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36695      */
36696     growMax : 800,
36697     /**
36698      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36699      */
36700     vtype : null,
36701     /**
36702      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36703      */
36704     maskRe : null,
36705     /**
36706      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36707      */
36708     disableKeyFilter : false,
36709     /**
36710      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36711      */
36712     allowBlank : true,
36713     /**
36714      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36715      */
36716     minLength : 0,
36717     /**
36718      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36719      */
36720     maxLength : Number.MAX_VALUE,
36721     /**
36722      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36723      */
36724     minLengthText : "The minimum length for this field is {0}",
36725     /**
36726      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36727      */
36728     maxLengthText : "The maximum length for this field is {0}",
36729     /**
36730      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36731      */
36732     selectOnFocus : false,
36733     /**
36734      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36735      */
36736     blankText : "This field is required",
36737     /**
36738      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36739      * If available, this function will be called only after the basic validators all return true, and will be passed the
36740      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36741      */
36742     validator : null,
36743     /**
36744      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36745      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36746      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36747      */
36748     regex : null,
36749     /**
36750      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36751      */
36752     regexText : "",
36753     /**
36754      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36755      */
36756     emptyText : null,
36757     /**
36758      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36759      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36760      */
36761     emptyClass : 'x-form-empty-field',
36762
36763     // private
36764     initEvents : function(){
36765         Roo.form.TextField.superclass.initEvents.call(this);
36766         if(this.validationEvent == 'keyup'){
36767             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36768             this.el.on('keyup', this.filterValidation, this);
36769         }
36770         else if(this.validationEvent !== false){
36771             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36772         }
36773         if(this.selectOnFocus || this.emptyText){
36774             this.on("focus", this.preFocus, this);
36775             if(this.emptyText){
36776                 this.on('blur', this.postBlur, this);
36777                 this.applyEmptyText();
36778             }
36779         }
36780         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36781             this.el.on("keypress", this.filterKeys, this);
36782         }
36783         if(this.grow){
36784             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36785             this.el.on("click", this.autoSize,  this);
36786         }
36787     },
36788
36789     processValue : function(value){
36790         if(this.stripCharsRe){
36791             var newValue = value.replace(this.stripCharsRe, '');
36792             if(newValue !== value){
36793                 this.setRawValue(newValue);
36794                 return newValue;
36795             }
36796         }
36797         return value;
36798     },
36799
36800     filterValidation : function(e){
36801         if(!e.isNavKeyPress()){
36802             this.validationTask.delay(this.validationDelay);
36803         }
36804     },
36805
36806     // private
36807     onKeyUp : function(e){
36808         if(!e.isNavKeyPress()){
36809             this.autoSize();
36810         }
36811     },
36812
36813     /**
36814      * Resets the current field value to the originally-loaded value and clears any validation messages.
36815      * Also adds emptyText and emptyClass if the original value was blank.
36816      */
36817     reset : function(){
36818         Roo.form.TextField.superclass.reset.call(this);
36819         this.applyEmptyText();
36820     },
36821
36822     applyEmptyText : function(){
36823         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36824             this.setRawValue(this.emptyText);
36825             this.el.addClass(this.emptyClass);
36826         }
36827     },
36828
36829     // private
36830     preFocus : function(){
36831         if(this.emptyText){
36832             if(this.el.dom.value == this.emptyText){
36833                 this.setRawValue('');
36834             }
36835             this.el.removeClass(this.emptyClass);
36836         }
36837         if(this.selectOnFocus){
36838             this.el.dom.select();
36839         }
36840     },
36841
36842     // private
36843     postBlur : function(){
36844         this.applyEmptyText();
36845     },
36846
36847     // private
36848     filterKeys : function(e){
36849         var k = e.getKey();
36850         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36851             return;
36852         }
36853         var c = e.getCharCode(), cc = String.fromCharCode(c);
36854         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36855             return;
36856         }
36857         if(!this.maskRe.test(cc)){
36858             e.stopEvent();
36859         }
36860     },
36861
36862     setValue : function(v){
36863         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36864             this.el.removeClass(this.emptyClass);
36865         }
36866         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36867         this.applyEmptyText();
36868         this.autoSize();
36869     },
36870
36871     /**
36872      * Validates a value according to the field's validation rules and marks the field as invalid
36873      * if the validation fails
36874      * @param {Mixed} value The value to validate
36875      * @return {Boolean} True if the value is valid, else false
36876      */
36877     validateValue : function(value){
36878         if(value.length < 1 || value === this.emptyText){ // if it's blank
36879              if(this.allowBlank){
36880                 this.clearInvalid();
36881                 return true;
36882              }else{
36883                 this.markInvalid(this.blankText);
36884                 return false;
36885              }
36886         }
36887         if(value.length < this.minLength){
36888             this.markInvalid(String.format(this.minLengthText, this.minLength));
36889             return false;
36890         }
36891         if(value.length > this.maxLength){
36892             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36893             return false;
36894         }
36895         if(this.vtype){
36896             var vt = Roo.form.VTypes;
36897             if(!vt[this.vtype](value, this)){
36898                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36899                 return false;
36900             }
36901         }
36902         if(typeof this.validator == "function"){
36903             var msg = this.validator(value);
36904             if(msg !== true){
36905                 this.markInvalid(msg);
36906                 return false;
36907             }
36908         }
36909         if(this.regex && !this.regex.test(value)){
36910             this.markInvalid(this.regexText);
36911             return false;
36912         }
36913         return true;
36914     },
36915
36916     /**
36917      * Selects text in this field
36918      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36919      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36920      */
36921     selectText : function(start, end){
36922         var v = this.getRawValue();
36923         if(v.length > 0){
36924             start = start === undefined ? 0 : start;
36925             end = end === undefined ? v.length : end;
36926             var d = this.el.dom;
36927             if(d.setSelectionRange){
36928                 d.setSelectionRange(start, end);
36929             }else if(d.createTextRange){
36930                 var range = d.createTextRange();
36931                 range.moveStart("character", start);
36932                 range.moveEnd("character", v.length-end);
36933                 range.select();
36934             }
36935         }
36936     },
36937
36938     /**
36939      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36940      * This only takes effect if grow = true, and fires the autosize event.
36941      */
36942     autoSize : function(){
36943         if(!this.grow || !this.rendered){
36944             return;
36945         }
36946         if(!this.metrics){
36947             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36948         }
36949         var el = this.el;
36950         var v = el.dom.value;
36951         var d = document.createElement('div');
36952         d.appendChild(document.createTextNode(v));
36953         v = d.innerHTML;
36954         d = null;
36955         v += "&#160;";
36956         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36957         this.el.setWidth(w);
36958         this.fireEvent("autosize", this, w);
36959     }
36960 });/*
36961  * Based on:
36962  * Ext JS Library 1.1.1
36963  * Copyright(c) 2006-2007, Ext JS, LLC.
36964  *
36965  * Originally Released Under LGPL - original licence link has changed is not relivant.
36966  *
36967  * Fork - LGPL
36968  * <script type="text/javascript">
36969  */
36970  
36971 /**
36972  * @class Roo.form.Hidden
36973  * @extends Roo.form.TextField
36974  * Simple Hidden element used on forms 
36975  * 
36976  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36977  * 
36978  * @constructor
36979  * Creates a new Hidden form element.
36980  * @param {Object} config Configuration options
36981  */
36982
36983
36984
36985 // easy hidden field...
36986 Roo.form.Hidden = function(config){
36987     Roo.form.Hidden.superclass.constructor.call(this, config);
36988 };
36989   
36990 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36991     fieldLabel:      '',
36992     inputType:      'hidden',
36993     width:          50,
36994     allowBlank:     true,
36995     labelSeparator: '',
36996     hidden:         true,
36997     itemCls :       'x-form-item-display-none'
36998
36999
37000 });
37001
37002
37003 /*
37004  * Based on:
37005  * Ext JS Library 1.1.1
37006  * Copyright(c) 2006-2007, Ext JS, LLC.
37007  *
37008  * Originally Released Under LGPL - original licence link has changed is not relivant.
37009  *
37010  * Fork - LGPL
37011  * <script type="text/javascript">
37012  */
37013  
37014 /**
37015  * @class Roo.form.TriggerField
37016  * @extends Roo.form.TextField
37017  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37018  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37019  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37020  * for which you can provide a custom implementation.  For example:
37021  * <pre><code>
37022 var trigger = new Roo.form.TriggerField();
37023 trigger.onTriggerClick = myTriggerFn;
37024 trigger.applyTo('my-field');
37025 </code></pre>
37026  *
37027  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37028  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37029  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37030  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37031  * @constructor
37032  * Create a new TriggerField.
37033  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37034  * to the base TextField)
37035  */
37036 Roo.form.TriggerField = function(config){
37037     this.mimicing = false;
37038     Roo.form.TriggerField.superclass.constructor.call(this, config);
37039 };
37040
37041 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37042     /**
37043      * @cfg {String} triggerClass A CSS class to apply to the trigger
37044      */
37045     /**
37046      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37047      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37048      */
37049     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37050     /**
37051      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37052      */
37053     hideTrigger:false,
37054
37055     /** @cfg {Boolean} grow @hide */
37056     /** @cfg {Number} growMin @hide */
37057     /** @cfg {Number} growMax @hide */
37058
37059     /**
37060      * @hide 
37061      * @method
37062      */
37063     autoSize: Roo.emptyFn,
37064     // private
37065     monitorTab : true,
37066     // private
37067     deferHeight : true,
37068
37069     
37070     actionMode : 'wrap',
37071     // private
37072     onResize : function(w, h){
37073         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37074         if(typeof w == 'number'){
37075             var x = w - this.trigger.getWidth();
37076             this.el.setWidth(this.adjustWidth('input', x));
37077             this.trigger.setStyle('left', x+'px');
37078         }
37079     },
37080
37081     // private
37082     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37083
37084     // private
37085     getResizeEl : function(){
37086         return this.wrap;
37087     },
37088
37089     // private
37090     getPositionEl : function(){
37091         return this.wrap;
37092     },
37093
37094     // private
37095     alignErrorIcon : function(){
37096         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37097     },
37098
37099     // private
37100     onRender : function(ct, position){
37101         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37102         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37103         this.trigger = this.wrap.createChild(this.triggerConfig ||
37104                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37105         if(this.hideTrigger){
37106             this.trigger.setDisplayed(false);
37107         }
37108         this.initTrigger();
37109         if(!this.width){
37110             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37111         }
37112     },
37113
37114     // private
37115     initTrigger : function(){
37116         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37117         this.trigger.addClassOnOver('x-form-trigger-over');
37118         this.trigger.addClassOnClick('x-form-trigger-click');
37119     },
37120
37121     // private
37122     onDestroy : function(){
37123         if(this.trigger){
37124             this.trigger.removeAllListeners();
37125             this.trigger.remove();
37126         }
37127         if(this.wrap){
37128             this.wrap.remove();
37129         }
37130         Roo.form.TriggerField.superclass.onDestroy.call(this);
37131     },
37132
37133     // private
37134     onFocus : function(){
37135         Roo.form.TriggerField.superclass.onFocus.call(this);
37136         if(!this.mimicing){
37137             this.wrap.addClass('x-trigger-wrap-focus');
37138             this.mimicing = true;
37139             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37140             if(this.monitorTab){
37141                 this.el.on("keydown", this.checkTab, this);
37142             }
37143         }
37144     },
37145
37146     // private
37147     checkTab : function(e){
37148         if(e.getKey() == e.TAB){
37149             this.triggerBlur();
37150         }
37151     },
37152
37153     // private
37154     onBlur : function(){
37155         // do nothing
37156     },
37157
37158     // private
37159     mimicBlur : function(e, t){
37160         if(!this.wrap.contains(t) && this.validateBlur()){
37161             this.triggerBlur();
37162         }
37163     },
37164
37165     // private
37166     triggerBlur : function(){
37167         this.mimicing = false;
37168         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37169         if(this.monitorTab){
37170             this.el.un("keydown", this.checkTab, this);
37171         }
37172         this.wrap.removeClass('x-trigger-wrap-focus');
37173         Roo.form.TriggerField.superclass.onBlur.call(this);
37174     },
37175
37176     // private
37177     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37178     validateBlur : function(e, t){
37179         return true;
37180     },
37181
37182     // private
37183     onDisable : function(){
37184         Roo.form.TriggerField.superclass.onDisable.call(this);
37185         if(this.wrap){
37186             this.wrap.addClass('x-item-disabled');
37187         }
37188     },
37189
37190     // private
37191     onEnable : function(){
37192         Roo.form.TriggerField.superclass.onEnable.call(this);
37193         if(this.wrap){
37194             this.wrap.removeClass('x-item-disabled');
37195         }
37196     },
37197
37198     // private
37199     onShow : function(){
37200         var ae = this.getActionEl();
37201         
37202         if(ae){
37203             ae.dom.style.display = '';
37204             ae.dom.style.visibility = 'visible';
37205         }
37206     },
37207
37208     // private
37209     
37210     onHide : function(){
37211         var ae = this.getActionEl();
37212         ae.dom.style.display = 'none';
37213     },
37214
37215     /**
37216      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37217      * by an implementing function.
37218      * @method
37219      * @param {EventObject} e
37220      */
37221     onTriggerClick : Roo.emptyFn
37222 });
37223
37224 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37225 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37226 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37227 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37228     initComponent : function(){
37229         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37230
37231         this.triggerConfig = {
37232             tag:'span', cls:'x-form-twin-triggers', cn:[
37233             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37234             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37235         ]};
37236     },
37237
37238     getTrigger : function(index){
37239         return this.triggers[index];
37240     },
37241
37242     initTrigger : function(){
37243         var ts = this.trigger.select('.x-form-trigger', true);
37244         this.wrap.setStyle('overflow', 'hidden');
37245         var triggerField = this;
37246         ts.each(function(t, all, index){
37247             t.hide = function(){
37248                 var w = triggerField.wrap.getWidth();
37249                 this.dom.style.display = 'none';
37250                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37251             };
37252             t.show = function(){
37253                 var w = triggerField.wrap.getWidth();
37254                 this.dom.style.display = '';
37255                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37256             };
37257             var triggerIndex = 'Trigger'+(index+1);
37258
37259             if(this['hide'+triggerIndex]){
37260                 t.dom.style.display = 'none';
37261             }
37262             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37263             t.addClassOnOver('x-form-trigger-over');
37264             t.addClassOnClick('x-form-trigger-click');
37265         }, this);
37266         this.triggers = ts.elements;
37267     },
37268
37269     onTrigger1Click : Roo.emptyFn,
37270     onTrigger2Click : Roo.emptyFn
37271 });/*
37272  * Based on:
37273  * Ext JS Library 1.1.1
37274  * Copyright(c) 2006-2007, Ext JS, LLC.
37275  *
37276  * Originally Released Under LGPL - original licence link has changed is not relivant.
37277  *
37278  * Fork - LGPL
37279  * <script type="text/javascript">
37280  */
37281  
37282 /**
37283  * @class Roo.form.TextArea
37284  * @extends Roo.form.TextField
37285  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37286  * support for auto-sizing.
37287  * @constructor
37288  * Creates a new TextArea
37289  * @param {Object} config Configuration options
37290  */
37291 Roo.form.TextArea = function(config){
37292     Roo.form.TextArea.superclass.constructor.call(this, config);
37293     // these are provided exchanges for backwards compat
37294     // minHeight/maxHeight were replaced by growMin/growMax to be
37295     // compatible with TextField growing config values
37296     if(this.minHeight !== undefined){
37297         this.growMin = this.minHeight;
37298     }
37299     if(this.maxHeight !== undefined){
37300         this.growMax = this.maxHeight;
37301     }
37302 };
37303
37304 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
37305     /**
37306      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
37307      */
37308     growMin : 60,
37309     /**
37310      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
37311      */
37312     growMax: 1000,
37313     /**
37314      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
37315      * in the field (equivalent to setting overflow: hidden, defaults to false)
37316      */
37317     preventScrollbars: false,
37318     /**
37319      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37320      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
37321      */
37322
37323     // private
37324     onRender : function(ct, position){
37325         if(!this.el){
37326             this.defaultAutoCreate = {
37327                 tag: "textarea",
37328                 style:"width:300px;height:60px;",
37329                 autocomplete: "off"
37330             };
37331         }
37332         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
37333         if(this.grow){
37334             this.textSizeEl = Roo.DomHelper.append(document.body, {
37335                 tag: "pre", cls: "x-form-grow-sizer"
37336             });
37337             if(this.preventScrollbars){
37338                 this.el.setStyle("overflow", "hidden");
37339             }
37340             this.el.setHeight(this.growMin);
37341         }
37342     },
37343
37344     onDestroy : function(){
37345         if(this.textSizeEl){
37346             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
37347         }
37348         Roo.form.TextArea.superclass.onDestroy.call(this);
37349     },
37350
37351     // private
37352     onKeyUp : function(e){
37353         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
37354             this.autoSize();
37355         }
37356     },
37357
37358     /**
37359      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
37360      * This only takes effect if grow = true, and fires the autosize event if the height changes.
37361      */
37362     autoSize : function(){
37363         if(!this.grow || !this.textSizeEl){
37364             return;
37365         }
37366         var el = this.el;
37367         var v = el.dom.value;
37368         var ts = this.textSizeEl;
37369
37370         ts.innerHTML = '';
37371         ts.appendChild(document.createTextNode(v));
37372         v = ts.innerHTML;
37373
37374         Roo.fly(ts).setWidth(this.el.getWidth());
37375         if(v.length < 1){
37376             v = "&#160;&#160;";
37377         }else{
37378             if(Roo.isIE){
37379                 v = v.replace(/\n/g, '<p>&#160;</p>');
37380             }
37381             v += "&#160;\n&#160;";
37382         }
37383         ts.innerHTML = v;
37384         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
37385         if(h != this.lastHeight){
37386             this.lastHeight = h;
37387             this.el.setHeight(h);
37388             this.fireEvent("autosize", this, h);
37389         }
37390     }
37391 });/*
37392  * Based on:
37393  * Ext JS Library 1.1.1
37394  * Copyright(c) 2006-2007, Ext JS, LLC.
37395  *
37396  * Originally Released Under LGPL - original licence link has changed is not relivant.
37397  *
37398  * Fork - LGPL
37399  * <script type="text/javascript">
37400  */
37401  
37402
37403 /**
37404  * @class Roo.form.NumberField
37405  * @extends Roo.form.TextField
37406  * Numeric text field that provides automatic keystroke filtering and numeric validation.
37407  * @constructor
37408  * Creates a new NumberField
37409  * @param {Object} config Configuration options
37410  */
37411 Roo.form.NumberField = function(config){
37412     Roo.form.NumberField.superclass.constructor.call(this, config);
37413 };
37414
37415 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
37416     /**
37417      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
37418      */
37419     fieldClass: "x-form-field x-form-num-field",
37420     /**
37421      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
37422      */
37423     allowDecimals : true,
37424     /**
37425      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
37426      */
37427     decimalSeparator : ".",
37428     /**
37429      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
37430      */
37431     decimalPrecision : 2,
37432     /**
37433      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
37434      */
37435     allowNegative : true,
37436     /**
37437      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
37438      */
37439     minValue : Number.NEGATIVE_INFINITY,
37440     /**
37441      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
37442      */
37443     maxValue : Number.MAX_VALUE,
37444     /**
37445      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37446      */
37447     minText : "The minimum value for this field is {0}",
37448     /**
37449      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37450      */
37451     maxText : "The maximum value for this field is {0}",
37452     /**
37453      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37454      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37455      */
37456     nanText : "{0} is not a valid number",
37457
37458     // private
37459     initEvents : function(){
37460         Roo.form.NumberField.superclass.initEvents.call(this);
37461         var allowed = "0123456789";
37462         if(this.allowDecimals){
37463             allowed += this.decimalSeparator;
37464         }
37465         if(this.allowNegative){
37466             allowed += "-";
37467         }
37468         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37469         var keyPress = function(e){
37470             var k = e.getKey();
37471             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37472                 return;
37473             }
37474             var c = e.getCharCode();
37475             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37476                 e.stopEvent();
37477             }
37478         };
37479         this.el.on("keypress", keyPress, this);
37480     },
37481
37482     // private
37483     validateValue : function(value){
37484         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
37485             return false;
37486         }
37487         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37488              return true;
37489         }
37490         var num = this.parseValue(value);
37491         if(isNaN(num)){
37492             this.markInvalid(String.format(this.nanText, value));
37493             return false;
37494         }
37495         if(num < this.minValue){
37496             this.markInvalid(String.format(this.minText, this.minValue));
37497             return false;
37498         }
37499         if(num > this.maxValue){
37500             this.markInvalid(String.format(this.maxText, this.maxValue));
37501             return false;
37502         }
37503         return true;
37504     },
37505
37506     getValue : function(){
37507         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
37508     },
37509
37510     // private
37511     parseValue : function(value){
37512         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37513         return isNaN(value) ? '' : value;
37514     },
37515
37516     // private
37517     fixPrecision : function(value){
37518         var nan = isNaN(value);
37519         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37520             return nan ? '' : value;
37521         }
37522         return parseFloat(value).toFixed(this.decimalPrecision);
37523     },
37524
37525     setValue : function(v){
37526         v = this.fixPrecision(v);
37527         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
37528     },
37529
37530     // private
37531     decimalPrecisionFcn : function(v){
37532         return Math.floor(v);
37533     },
37534
37535     beforeBlur : function(){
37536         var v = this.parseValue(this.getRawValue());
37537         if(v){
37538             this.setValue(v);
37539         }
37540     }
37541 });/*
37542  * Based on:
37543  * Ext JS Library 1.1.1
37544  * Copyright(c) 2006-2007, Ext JS, LLC.
37545  *
37546  * Originally Released Under LGPL - original licence link has changed is not relivant.
37547  *
37548  * Fork - LGPL
37549  * <script type="text/javascript">
37550  */
37551  
37552 /**
37553  * @class Roo.form.DateField
37554  * @extends Roo.form.TriggerField
37555  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37556 * @constructor
37557 * Create a new DateField
37558 * @param {Object} config
37559  */
37560 Roo.form.DateField = function(config){
37561     Roo.form.DateField.superclass.constructor.call(this, config);
37562     
37563       this.addEvents({
37564          
37565         /**
37566          * @event select
37567          * Fires when a date is selected
37568              * @param {Roo.form.DateField} combo This combo box
37569              * @param {Date} date The date selected
37570              */
37571         'select' : true
37572          
37573     });
37574     
37575     
37576     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37577     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37578     this.ddMatch = null;
37579     if(this.disabledDates){
37580         var dd = this.disabledDates;
37581         var re = "(?:";
37582         for(var i = 0; i < dd.length; i++){
37583             re += dd[i];
37584             if(i != dd.length-1) re += "|";
37585         }
37586         this.ddMatch = new RegExp(re + ")");
37587     }
37588 };
37589
37590 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
37591     /**
37592      * @cfg {String} format
37593      * The default date format string which can be overriden for localization support.  The format must be
37594      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37595      */
37596     format : "m/d/y",
37597     /**
37598      * @cfg {String} altFormats
37599      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37600      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37601      */
37602     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37603     /**
37604      * @cfg {Array} disabledDays
37605      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37606      */
37607     disabledDays : null,
37608     /**
37609      * @cfg {String} disabledDaysText
37610      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37611      */
37612     disabledDaysText : "Disabled",
37613     /**
37614      * @cfg {Array} disabledDates
37615      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37616      * expression so they are very powerful. Some examples:
37617      * <ul>
37618      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37619      * <li>["03/08", "09/16"] would disable those days for every year</li>
37620      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37621      * <li>["03/../2006"] would disable every day in March 2006</li>
37622      * <li>["^03"] would disable every day in every March</li>
37623      * </ul>
37624      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37625      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37626      */
37627     disabledDates : null,
37628     /**
37629      * @cfg {String} disabledDatesText
37630      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37631      */
37632     disabledDatesText : "Disabled",
37633     /**
37634      * @cfg {Date/String} minValue
37635      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37636      * valid format (defaults to null).
37637      */
37638     minValue : null,
37639     /**
37640      * @cfg {Date/String} maxValue
37641      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37642      * valid format (defaults to null).
37643      */
37644     maxValue : null,
37645     /**
37646      * @cfg {String} minText
37647      * The error text to display when the date in the cell is before minValue (defaults to
37648      * 'The date in this field must be after {minValue}').
37649      */
37650     minText : "The date in this field must be equal to or after {0}",
37651     /**
37652      * @cfg {String} maxText
37653      * The error text to display when the date in the cell is after maxValue (defaults to
37654      * 'The date in this field must be before {maxValue}').
37655      */
37656     maxText : "The date in this field must be equal to or before {0}",
37657     /**
37658      * @cfg {String} invalidText
37659      * The error text to display when the date in the field is invalid (defaults to
37660      * '{value} is not a valid date - it must be in the format {format}').
37661      */
37662     invalidText : "{0} is not a valid date - it must be in the format {1}",
37663     /**
37664      * @cfg {String} triggerClass
37665      * An additional CSS class used to style the trigger button.  The trigger will always get the
37666      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37667      * which displays a calendar icon).
37668      */
37669     triggerClass : 'x-form-date-trigger',
37670     
37671
37672     /**
37673      * @cfg {Boolean} useIso
37674      * if enabled, then the date field will use a hidden field to store the 
37675      * real value as iso formated date. default (false)
37676      */ 
37677     useIso : false,
37678     /**
37679      * @cfg {String/Object} autoCreate
37680      * A DomHelper element spec, or true for a default element spec (defaults to
37681      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37682      */ 
37683     // private
37684     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37685     
37686     // private
37687     hiddenField: false,
37688     
37689     onRender : function(ct, position)
37690     {
37691         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37692         if (this.useIso) {
37693             this.el.dom.removeAttribute('name'); 
37694             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37695                     'before', true);
37696             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
37697             // prevent input submission
37698             this.hiddenName = this.name;
37699         }
37700             
37701             
37702     },
37703     
37704     // private
37705     validateValue : function(value)
37706     {
37707         value = this.formatDate(value);
37708         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37709             return false;
37710         }
37711         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37712              return true;
37713         }
37714         var svalue = value;
37715         value = this.parseDate(value);
37716         if(!value){
37717             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37718             return false;
37719         }
37720         var time = value.getTime();
37721         if(this.minValue && time < this.minValue.getTime()){
37722             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37723             return false;
37724         }
37725         if(this.maxValue && time > this.maxValue.getTime()){
37726             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37727             return false;
37728         }
37729         if(this.disabledDays){
37730             var day = value.getDay();
37731             for(var i = 0; i < this.disabledDays.length; i++) {
37732                 if(day === this.disabledDays[i]){
37733                     this.markInvalid(this.disabledDaysText);
37734                     return false;
37735                 }
37736             }
37737         }
37738         var fvalue = this.formatDate(value);
37739         if(this.ddMatch && this.ddMatch.test(fvalue)){
37740             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37741             return false;
37742         }
37743         return true;
37744     },
37745
37746     // private
37747     // Provides logic to override the default TriggerField.validateBlur which just returns true
37748     validateBlur : function(){
37749         return !this.menu || !this.menu.isVisible();
37750     },
37751
37752     /**
37753      * Returns the current date value of the date field.
37754      * @return {Date} The date value
37755      */
37756     getValue : function(){
37757         
37758         return  this.hiddenField ?
37759                 this.hiddenField.value :
37760                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37761     },
37762
37763     /**
37764      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37765      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37766      * (the default format used is "m/d/y").
37767      * <br />Usage:
37768      * <pre><code>
37769 //All of these calls set the same date value (May 4, 2006)
37770
37771 //Pass a date object:
37772 var dt = new Date('5/4/06');
37773 dateField.setValue(dt);
37774
37775 //Pass a date string (default format):
37776 dateField.setValue('5/4/06');
37777
37778 //Pass a date string (custom format):
37779 dateField.format = 'Y-m-d';
37780 dateField.setValue('2006-5-4');
37781 </code></pre>
37782      * @param {String/Date} date The date or valid date string
37783      */
37784     setValue : function(date){
37785         if (this.hiddenField) {
37786             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37787         }
37788         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37789     },
37790
37791     // private
37792     parseDate : function(value){
37793         if(!value || value instanceof Date){
37794             return value;
37795         }
37796         var v = Date.parseDate(value, this.format);
37797         if(!v && this.altFormats){
37798             if(!this.altFormatsArray){
37799                 this.altFormatsArray = this.altFormats.split("|");
37800             }
37801             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37802                 v = Date.parseDate(value, this.altFormatsArray[i]);
37803             }
37804         }
37805         return v;
37806     },
37807
37808     // private
37809     formatDate : function(date, fmt){
37810         return (!date || !(date instanceof Date)) ?
37811                date : date.dateFormat(fmt || this.format);
37812     },
37813
37814     // private
37815     menuListeners : {
37816         select: function(m, d){
37817             this.setValue(d);
37818             this.fireEvent('select', this, d);
37819         },
37820         show : function(){ // retain focus styling
37821             this.onFocus();
37822         },
37823         hide : function(){
37824             this.focus.defer(10, this);
37825             var ml = this.menuListeners;
37826             this.menu.un("select", ml.select,  this);
37827             this.menu.un("show", ml.show,  this);
37828             this.menu.un("hide", ml.hide,  this);
37829         }
37830     },
37831
37832     // private
37833     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37834     onTriggerClick : function(){
37835         if(this.disabled){
37836             return;
37837         }
37838         if(this.menu == null){
37839             this.menu = new Roo.menu.DateMenu();
37840         }
37841         Roo.apply(this.menu.picker,  {
37842             showClear: this.allowBlank,
37843             minDate : this.minValue,
37844             maxDate : this.maxValue,
37845             disabledDatesRE : this.ddMatch,
37846             disabledDatesText : this.disabledDatesText,
37847             disabledDays : this.disabledDays,
37848             disabledDaysText : this.disabledDaysText,
37849             format : this.format,
37850             minText : String.format(this.minText, this.formatDate(this.minValue)),
37851             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37852         });
37853         this.menu.on(Roo.apply({}, this.menuListeners, {
37854             scope:this
37855         }));
37856         this.menu.picker.setValue(this.getValue() || new Date());
37857         this.menu.show(this.el, "tl-bl?");
37858     },
37859
37860     beforeBlur : function(){
37861         var v = this.parseDate(this.getRawValue());
37862         if(v){
37863             this.setValue(v);
37864         }
37865     }
37866
37867     /** @cfg {Boolean} grow @hide */
37868     /** @cfg {Number} growMin @hide */
37869     /** @cfg {Number} growMax @hide */
37870     /**
37871      * @hide
37872      * @method autoSize
37873      */
37874 });/*
37875  * Based on:
37876  * Ext JS Library 1.1.1
37877  * Copyright(c) 2006-2007, Ext JS, LLC.
37878  *
37879  * Originally Released Under LGPL - original licence link has changed is not relivant.
37880  *
37881  * Fork - LGPL
37882  * <script type="text/javascript">
37883  */
37884  
37885
37886 /**
37887  * @class Roo.form.ComboBox
37888  * @extends Roo.form.TriggerField
37889  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37890  * @constructor
37891  * Create a new ComboBox.
37892  * @param {Object} config Configuration options
37893  */
37894 Roo.form.ComboBox = function(config){
37895     Roo.form.ComboBox.superclass.constructor.call(this, config);
37896     this.addEvents({
37897         /**
37898          * @event expand
37899          * Fires when the dropdown list is expanded
37900              * @param {Roo.form.ComboBox} combo This combo box
37901              */
37902         'expand' : true,
37903         /**
37904          * @event collapse
37905          * Fires when the dropdown list is collapsed
37906              * @param {Roo.form.ComboBox} combo This combo box
37907              */
37908         'collapse' : true,
37909         /**
37910          * @event beforeselect
37911          * Fires before a list item is selected. Return false to cancel the selection.
37912              * @param {Roo.form.ComboBox} combo This combo box
37913              * @param {Roo.data.Record} record The data record returned from the underlying store
37914              * @param {Number} index The index of the selected item in the dropdown list
37915              */
37916         'beforeselect' : true,
37917         /**
37918          * @event select
37919          * Fires when a list item is selected
37920              * @param {Roo.form.ComboBox} combo This combo box
37921              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37922              * @param {Number} index The index of the selected item in the dropdown list
37923              */
37924         'select' : true,
37925         /**
37926          * @event beforequery
37927          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37928          * The event object passed has these properties:
37929              * @param {Roo.form.ComboBox} combo This combo box
37930              * @param {String} query The query
37931              * @param {Boolean} forceAll true to force "all" query
37932              * @param {Boolean} cancel true to cancel the query
37933              * @param {Object} e The query event object
37934              */
37935         'beforequery': true,
37936          /**
37937          * @event add
37938          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37939              * @param {Roo.form.ComboBox} combo This combo box
37940              */
37941         'add' : true,
37942         /**
37943          * @event edit
37944          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37945              * @param {Roo.form.ComboBox} combo This combo box
37946              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37947              */
37948         'edit' : true
37949         
37950         
37951     });
37952     if(this.transform){
37953         this.allowDomMove = false;
37954         var s = Roo.getDom(this.transform);
37955         if(!this.hiddenName){
37956             this.hiddenName = s.name;
37957         }
37958         if(!this.store){
37959             this.mode = 'local';
37960             var d = [], opts = s.options;
37961             for(var i = 0, len = opts.length;i < len; i++){
37962                 var o = opts[i];
37963                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37964                 if(o.selected) {
37965                     this.value = value;
37966                 }
37967                 d.push([value, o.text]);
37968             }
37969             this.store = new Roo.data.SimpleStore({
37970                 'id': 0,
37971                 fields: ['value', 'text'],
37972                 data : d
37973             });
37974             this.valueField = 'value';
37975             this.displayField = 'text';
37976         }
37977         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37978         if(!this.lazyRender){
37979             this.target = true;
37980             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37981             s.parentNode.removeChild(s); // remove it
37982             this.render(this.el.parentNode);
37983         }else{
37984             s.parentNode.removeChild(s); // remove it
37985         }
37986
37987     }
37988     if (this.store) {
37989         this.store = Roo.factory(this.store, Roo.data);
37990     }
37991     
37992     this.selectedIndex = -1;
37993     if(this.mode == 'local'){
37994         if(config.queryDelay === undefined){
37995             this.queryDelay = 10;
37996         }
37997         if(config.minChars === undefined){
37998             this.minChars = 0;
37999         }
38000     }
38001 };
38002
38003 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
38004     /**
38005      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
38006      */
38007     /**
38008      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
38009      * rendering into an Roo.Editor, defaults to false)
38010      */
38011     /**
38012      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
38013      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
38014      */
38015     /**
38016      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
38017      */
38018     /**
38019      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
38020      * the dropdown list (defaults to undefined, with no header element)
38021      */
38022
38023      /**
38024      * @cfg {String/Roo.Template} tpl The template to use to render the output
38025      */
38026      
38027     // private
38028     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
38029     /**
38030      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
38031      */
38032     listWidth: undefined,
38033     /**
38034      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
38035      * mode = 'remote' or 'text' if mode = 'local')
38036      */
38037     displayField: undefined,
38038     /**
38039      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
38040      * mode = 'remote' or 'value' if mode = 'local'). 
38041      * Note: use of a valueField requires the user make a selection
38042      * in order for a value to be mapped.
38043      */
38044     valueField: undefined,
38045     
38046     
38047     /**
38048      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
38049      * field's data value (defaults to the underlying DOM element's name)
38050      */
38051     hiddenName: undefined,
38052     /**
38053      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
38054      */
38055     listClass: '',
38056     /**
38057      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
38058      */
38059     selectedClass: 'x-combo-selected',
38060     /**
38061      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38062      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
38063      * which displays a downward arrow icon).
38064      */
38065     triggerClass : 'x-form-arrow-trigger',
38066     /**
38067      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
38068      */
38069     shadow:'sides',
38070     /**
38071      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
38072      * anchor positions (defaults to 'tl-bl')
38073      */
38074     listAlign: 'tl-bl?',
38075     /**
38076      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
38077      */
38078     maxHeight: 300,
38079     /**
38080      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
38081      * query specified by the allQuery config option (defaults to 'query')
38082      */
38083     triggerAction: 'query',
38084     /**
38085      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
38086      * (defaults to 4, does not apply if editable = false)
38087      */
38088     minChars : 4,
38089     /**
38090      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
38091      * delay (typeAheadDelay) if it matches a known value (defaults to false)
38092      */
38093     typeAhead: false,
38094     /**
38095      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
38096      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
38097      */
38098     queryDelay: 500,
38099     /**
38100      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
38101      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
38102      */
38103     pageSize: 0,
38104     /**
38105      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
38106      * when editable = true (defaults to false)
38107      */
38108     selectOnFocus:false,
38109     /**
38110      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
38111      */
38112     queryParam: 'query',
38113     /**
38114      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
38115      * when mode = 'remote' (defaults to 'Loading...')
38116      */
38117     loadingText: 'Loading...',
38118     /**
38119      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
38120      */
38121     resizable: false,
38122     /**
38123      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
38124      */
38125     handleHeight : 8,
38126     /**
38127      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
38128      * traditional select (defaults to true)
38129      */
38130     editable: true,
38131     /**
38132      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
38133      */
38134     allQuery: '',
38135     /**
38136      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
38137      */
38138     mode: 'remote',
38139     /**
38140      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
38141      * listWidth has a higher value)
38142      */
38143     minListWidth : 70,
38144     /**
38145      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
38146      * allow the user to set arbitrary text into the field (defaults to false)
38147      */
38148     forceSelection:false,
38149     /**
38150      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
38151      * if typeAhead = true (defaults to 250)
38152      */
38153     typeAheadDelay : 250,
38154     /**
38155      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
38156      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
38157      */
38158     valueNotFoundText : undefined,
38159     /**
38160      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
38161      */
38162     blockFocus : false,
38163     
38164     /**
38165      * @cfg {Boolean} disableClear Disable showing of clear button.
38166      */
38167     disableClear : false,
38168     /**
38169      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
38170      */
38171     alwaysQuery : false,
38172     
38173     //private
38174     addicon : false,
38175     editicon: false,
38176     
38177     // element that contains real text value.. (when hidden is used..)
38178      
38179     // private
38180     onRender : function(ct, position){
38181         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
38182         if(this.hiddenName){
38183             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
38184                     'before', true);
38185             this.hiddenField.value =
38186                 this.hiddenValue !== undefined ? this.hiddenValue :
38187                 this.value !== undefined ? this.value : '';
38188
38189             // prevent input submission
38190             this.el.dom.removeAttribute('name');
38191              
38192              
38193         }
38194         if(Roo.isGecko){
38195             this.el.dom.setAttribute('autocomplete', 'off');
38196         }
38197
38198         var cls = 'x-combo-list';
38199
38200         this.list = new Roo.Layer({
38201             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
38202         });
38203
38204         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
38205         this.list.setWidth(lw);
38206         this.list.swallowEvent('mousewheel');
38207         this.assetHeight = 0;
38208
38209         if(this.title){
38210             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
38211             this.assetHeight += this.header.getHeight();
38212         }
38213
38214         this.innerList = this.list.createChild({cls:cls+'-inner'});
38215         this.innerList.on('mouseover', this.onViewOver, this);
38216         this.innerList.on('mousemove', this.onViewMove, this);
38217         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38218         
38219         if(this.allowBlank && !this.pageSize && !this.disableClear){
38220             this.footer = this.list.createChild({cls:cls+'-ft'});
38221             this.pageTb = new Roo.Toolbar(this.footer);
38222            
38223         }
38224         if(this.pageSize){
38225             this.footer = this.list.createChild({cls:cls+'-ft'});
38226             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
38227                     {pageSize: this.pageSize});
38228             
38229         }
38230         
38231         if (this.pageTb && this.allowBlank && !this.disableClear) {
38232             var _this = this;
38233             this.pageTb.add(new Roo.Toolbar.Fill(), {
38234                 cls: 'x-btn-icon x-btn-clear',
38235                 text: '&#160;',
38236                 handler: function()
38237                 {
38238                     _this.collapse();
38239                     _this.clearValue();
38240                     _this.onSelect(false, -1);
38241                 }
38242             });
38243         }
38244         if (this.footer) {
38245             this.assetHeight += this.footer.getHeight();
38246         }
38247         
38248
38249         if(!this.tpl){
38250             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
38251         }
38252
38253         this.view = new Roo.View(this.innerList, this.tpl, {
38254             singleSelect:true, store: this.store, selectedClass: this.selectedClass
38255         });
38256
38257         this.view.on('click', this.onViewClick, this);
38258
38259         this.store.on('beforeload', this.onBeforeLoad, this);
38260         this.store.on('load', this.onLoad, this);
38261         this.store.on('loadexception', this.onLoadException, this);
38262
38263         if(this.resizable){
38264             this.resizer = new Roo.Resizable(this.list,  {
38265                pinned:true, handles:'se'
38266             });
38267             this.resizer.on('resize', function(r, w, h){
38268                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
38269                 this.listWidth = w;
38270                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
38271                 this.restrictHeight();
38272             }, this);
38273             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
38274         }
38275         if(!this.editable){
38276             this.editable = true;
38277             this.setEditable(false);
38278         }  
38279         
38280         
38281         if (typeof(this.events.add.listeners) != 'undefined') {
38282             
38283             this.addicon = this.wrap.createChild(
38284                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
38285        
38286             this.addicon.on('click', function(e) {
38287                 this.fireEvent('add', this);
38288             }, this);
38289         }
38290         if (typeof(this.events.edit.listeners) != 'undefined') {
38291             
38292             this.editicon = this.wrap.createChild(
38293                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
38294             if (this.addicon) {
38295                 this.editicon.setStyle('margin-left', '40px');
38296             }
38297             this.editicon.on('click', function(e) {
38298                 
38299                 // we fire even  if inothing is selected..
38300                 this.fireEvent('edit', this, this.lastData );
38301                 
38302             }, this);
38303         }
38304         
38305         
38306         
38307     },
38308
38309     // private
38310     initEvents : function(){
38311         Roo.form.ComboBox.superclass.initEvents.call(this);
38312
38313         this.keyNav = new Roo.KeyNav(this.el, {
38314             "up" : function(e){
38315                 this.inKeyMode = true;
38316                 this.selectPrev();
38317             },
38318
38319             "down" : function(e){
38320                 if(!this.isExpanded()){
38321                     this.onTriggerClick();
38322                 }else{
38323                     this.inKeyMode = true;
38324                     this.selectNext();
38325                 }
38326             },
38327
38328             "enter" : function(e){
38329                 this.onViewClick();
38330                 //return true;
38331             },
38332
38333             "esc" : function(e){
38334                 this.collapse();
38335             },
38336
38337             "tab" : function(e){
38338                 this.onViewClick(false);
38339                 this.fireEvent("specialkey", this, e);
38340                 return true;
38341             },
38342
38343             scope : this,
38344
38345             doRelay : function(foo, bar, hname){
38346                 if(hname == 'down' || this.scope.isExpanded()){
38347                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38348                 }
38349                 return true;
38350             },
38351
38352             forceKeyDown: true
38353         });
38354         this.queryDelay = Math.max(this.queryDelay || 10,
38355                 this.mode == 'local' ? 10 : 250);
38356         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
38357         if(this.typeAhead){
38358             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
38359         }
38360         if(this.editable !== false){
38361             this.el.on("keyup", this.onKeyUp, this);
38362         }
38363         if(this.forceSelection){
38364             this.on('blur', this.doForce, this);
38365         }
38366     },
38367
38368     onDestroy : function(){
38369         if(this.view){
38370             this.view.setStore(null);
38371             this.view.el.removeAllListeners();
38372             this.view.el.remove();
38373             this.view.purgeListeners();
38374         }
38375         if(this.list){
38376             this.list.destroy();
38377         }
38378         if(this.store){
38379             this.store.un('beforeload', this.onBeforeLoad, this);
38380             this.store.un('load', this.onLoad, this);
38381             this.store.un('loadexception', this.onLoadException, this);
38382         }
38383         Roo.form.ComboBox.superclass.onDestroy.call(this);
38384     },
38385
38386     // private
38387     fireKey : function(e){
38388         if(e.isNavKeyPress() && !this.list.isVisible()){
38389             this.fireEvent("specialkey", this, e);
38390         }
38391     },
38392
38393     // private
38394     onResize: function(w, h){
38395         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
38396         
38397         if(typeof w != 'number'){
38398             // we do not handle it!?!?
38399             return;
38400         }
38401         var tw = this.trigger.getWidth();
38402         tw += this.addicon ? this.addicon.getWidth() : 0;
38403         tw += this.editicon ? this.editicon.getWidth() : 0;
38404         var x = w - tw;
38405         this.el.setWidth( this.adjustWidth('input', x));
38406             
38407         this.trigger.setStyle('left', x+'px');
38408         
38409         if(this.list && this.listWidth === undefined){
38410             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
38411             this.list.setWidth(lw);
38412             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38413         }
38414         
38415     
38416         
38417     },
38418
38419     /**
38420      * Allow or prevent the user from directly editing the field text.  If false is passed,
38421      * the user will only be able to select from the items defined in the dropdown list.  This method
38422      * is the runtime equivalent of setting the 'editable' config option at config time.
38423      * @param {Boolean} value True to allow the user to directly edit the field text
38424      */
38425     setEditable : function(value){
38426         if(value == this.editable){
38427             return;
38428         }
38429         this.editable = value;
38430         if(!value){
38431             this.el.dom.setAttribute('readOnly', true);
38432             this.el.on('mousedown', this.onTriggerClick,  this);
38433             this.el.addClass('x-combo-noedit');
38434         }else{
38435             this.el.dom.setAttribute('readOnly', false);
38436             this.el.un('mousedown', this.onTriggerClick,  this);
38437             this.el.removeClass('x-combo-noedit');
38438         }
38439     },
38440
38441     // private
38442     onBeforeLoad : function(){
38443         if(!this.hasFocus){
38444             return;
38445         }
38446         this.innerList.update(this.loadingText ?
38447                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
38448         this.restrictHeight();
38449         this.selectedIndex = -1;
38450     },
38451
38452     // private
38453     onLoad : function(){
38454         if(!this.hasFocus){
38455             return;
38456         }
38457         if(this.store.getCount() > 0){
38458             this.expand();
38459             this.restrictHeight();
38460             if(this.lastQuery == this.allQuery){
38461                 if(this.editable){
38462                     this.el.dom.select();
38463                 }
38464                 if(!this.selectByValue(this.value, true)){
38465                     this.select(0, true);
38466                 }
38467             }else{
38468                 this.selectNext();
38469                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
38470                     this.taTask.delay(this.typeAheadDelay);
38471                 }
38472             }
38473         }else{
38474             this.onEmptyResults();
38475         }
38476         //this.el.focus();
38477     },
38478     // private
38479     onLoadException : function()
38480     {
38481         this.collapse();
38482         Roo.log(this.store.reader.jsonData);
38483         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38484             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38485         }
38486         
38487         
38488     },
38489     // private
38490     onTypeAhead : function(){
38491         if(this.store.getCount() > 0){
38492             var r = this.store.getAt(0);
38493             var newValue = r.data[this.displayField];
38494             var len = newValue.length;
38495             var selStart = this.getRawValue().length;
38496             if(selStart != len){
38497                 this.setRawValue(newValue);
38498                 this.selectText(selStart, newValue.length);
38499             }
38500         }
38501     },
38502
38503     // private
38504     onSelect : function(record, index){
38505         if(this.fireEvent('beforeselect', this, record, index) !== false){
38506             this.setFromData(index > -1 ? record.data : false);
38507             this.collapse();
38508             this.fireEvent('select', this, record, index);
38509         }
38510     },
38511
38512     /**
38513      * Returns the currently selected field value or empty string if no value is set.
38514      * @return {String} value The selected value
38515      */
38516     getValue : function(){
38517         if(this.valueField){
38518             return typeof this.value != 'undefined' ? this.value : '';
38519         }else{
38520             return Roo.form.ComboBox.superclass.getValue.call(this);
38521         }
38522     },
38523
38524     /**
38525      * Clears any text/value currently set in the field
38526      */
38527     clearValue : function(){
38528         if(this.hiddenField){
38529             this.hiddenField.value = '';
38530         }
38531         this.value = '';
38532         this.setRawValue('');
38533         this.lastSelectionText = '';
38534         this.applyEmptyText();
38535     },
38536
38537     /**
38538      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
38539      * will be displayed in the field.  If the value does not match the data value of an existing item,
38540      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
38541      * Otherwise the field will be blank (although the value will still be set).
38542      * @param {String} value The value to match
38543      */
38544     setValue : function(v){
38545         var text = v;
38546         if(this.valueField){
38547             var r = this.findRecord(this.valueField, v);
38548             if(r){
38549                 text = r.data[this.displayField];
38550             }else if(this.valueNotFoundText !== undefined){
38551                 text = this.valueNotFoundText;
38552             }
38553         }
38554         this.lastSelectionText = text;
38555         if(this.hiddenField){
38556             this.hiddenField.value = v;
38557         }
38558         Roo.form.ComboBox.superclass.setValue.call(this, text);
38559         this.value = v;
38560     },
38561     /**
38562      * @property {Object} the last set data for the element
38563      */
38564     
38565     lastData : false,
38566     /**
38567      * Sets the value of the field based on a object which is related to the record format for the store.
38568      * @param {Object} value the value to set as. or false on reset?
38569      */
38570     setFromData : function(o){
38571         var dv = ''; // display value
38572         var vv = ''; // value value..
38573         this.lastData = o;
38574         if (this.displayField) {
38575             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
38576         } else {
38577             // this is an error condition!!!
38578             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
38579         }
38580         
38581         if(this.valueField){
38582             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
38583         }
38584         if(this.hiddenField){
38585             this.hiddenField.value = vv;
38586             
38587             this.lastSelectionText = dv;
38588             Roo.form.ComboBox.superclass.setValue.call(this, dv);
38589             this.value = vv;
38590             return;
38591         }
38592         // no hidden field.. - we store the value in 'value', but still display
38593         // display field!!!!
38594         this.lastSelectionText = dv;
38595         Roo.form.ComboBox.superclass.setValue.call(this, dv);
38596         this.value = vv;
38597         
38598         
38599     },
38600     // private
38601     reset : function(){
38602         // overridden so that last data is reset..
38603         this.setValue(this.originalValue);
38604         this.clearInvalid();
38605         this.lastData = false;
38606     },
38607     // private
38608     findRecord : function(prop, value){
38609         var record;
38610         if(this.store.getCount() > 0){
38611             this.store.each(function(r){
38612                 if(r.data[prop] == value){
38613                     record = r;
38614                     return false;
38615                 }
38616                 return true;
38617             });
38618         }
38619         return record;
38620     },
38621     
38622     getName: function()
38623     {
38624         // returns hidden if it's set..
38625         if (!this.rendered) {return ''};
38626         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38627         
38628     },
38629     // private
38630     onViewMove : function(e, t){
38631         this.inKeyMode = false;
38632     },
38633
38634     // private
38635     onViewOver : function(e, t){
38636         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38637             return;
38638         }
38639         var item = this.view.findItemFromChild(t);
38640         if(item){
38641             var index = this.view.indexOf(item);
38642             this.select(index, false);
38643         }
38644     },
38645
38646     // private
38647     onViewClick : function(doFocus)
38648     {
38649         var index = this.view.getSelectedIndexes()[0];
38650         var r = this.store.getAt(index);
38651         if(r){
38652             this.onSelect(r, index);
38653         }
38654         if(doFocus !== false && !this.blockFocus){
38655             this.el.focus();
38656         }
38657     },
38658
38659     // private
38660     restrictHeight : function(){
38661         this.innerList.dom.style.height = '';
38662         var inner = this.innerList.dom;
38663         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38664         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38665         this.list.beginUpdate();
38666         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38667         this.list.alignTo(this.el, this.listAlign);
38668         this.list.endUpdate();
38669     },
38670
38671     // private
38672     onEmptyResults : function(){
38673         this.collapse();
38674     },
38675
38676     /**
38677      * Returns true if the dropdown list is expanded, else false.
38678      */
38679     isExpanded : function(){
38680         return this.list.isVisible();
38681     },
38682
38683     /**
38684      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38685      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38686      * @param {String} value The data value of the item to select
38687      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38688      * selected item if it is not currently in view (defaults to true)
38689      * @return {Boolean} True if the value matched an item in the list, else false
38690      */
38691     selectByValue : function(v, scrollIntoView){
38692         if(v !== undefined && v !== null){
38693             var r = this.findRecord(this.valueField || this.displayField, v);
38694             if(r){
38695                 this.select(this.store.indexOf(r), scrollIntoView);
38696                 return true;
38697             }
38698         }
38699         return false;
38700     },
38701
38702     /**
38703      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38704      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38705      * @param {Number} index The zero-based index of the list item to select
38706      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38707      * selected item if it is not currently in view (defaults to true)
38708      */
38709     select : function(index, scrollIntoView){
38710         this.selectedIndex = index;
38711         this.view.select(index);
38712         if(scrollIntoView !== false){
38713             var el = this.view.getNode(index);
38714             if(el){
38715                 this.innerList.scrollChildIntoView(el, false);
38716             }
38717         }
38718     },
38719
38720     // private
38721     selectNext : function(){
38722         var ct = this.store.getCount();
38723         if(ct > 0){
38724             if(this.selectedIndex == -1){
38725                 this.select(0);
38726             }else if(this.selectedIndex < ct-1){
38727                 this.select(this.selectedIndex+1);
38728             }
38729         }
38730     },
38731
38732     // private
38733     selectPrev : function(){
38734         var ct = this.store.getCount();
38735         if(ct > 0){
38736             if(this.selectedIndex == -1){
38737                 this.select(0);
38738             }else if(this.selectedIndex != 0){
38739                 this.select(this.selectedIndex-1);
38740             }
38741         }
38742     },
38743
38744     // private
38745     onKeyUp : function(e){
38746         if(this.editable !== false && !e.isSpecialKey()){
38747             this.lastKey = e.getKey();
38748             this.dqTask.delay(this.queryDelay);
38749         }
38750     },
38751
38752     // private
38753     validateBlur : function(){
38754         return !this.list || !this.list.isVisible();   
38755     },
38756
38757     // private
38758     initQuery : function(){
38759         this.doQuery(this.getRawValue());
38760     },
38761
38762     // private
38763     doForce : function(){
38764         if(this.el.dom.value.length > 0){
38765             this.el.dom.value =
38766                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38767             this.applyEmptyText();
38768         }
38769     },
38770
38771     /**
38772      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38773      * query allowing the query action to be canceled if needed.
38774      * @param {String} query The SQL query to execute
38775      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38776      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38777      * saved in the current store (defaults to false)
38778      */
38779     doQuery : function(q, forceAll){
38780         if(q === undefined || q === null){
38781             q = '';
38782         }
38783         var qe = {
38784             query: q,
38785             forceAll: forceAll,
38786             combo: this,
38787             cancel:false
38788         };
38789         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38790             return false;
38791         }
38792         q = qe.query;
38793         forceAll = qe.forceAll;
38794         if(forceAll === true || (q.length >= this.minChars)){
38795             if(this.lastQuery != q || this.alwaysQuery){
38796                 this.lastQuery = q;
38797                 if(this.mode == 'local'){
38798                     this.selectedIndex = -1;
38799                     if(forceAll){
38800                         this.store.clearFilter();
38801                     }else{
38802                         this.store.filter(this.displayField, q);
38803                     }
38804                     this.onLoad();
38805                 }else{
38806                     this.store.baseParams[this.queryParam] = q;
38807                     this.store.load({
38808                         params: this.getParams(q)
38809                     });
38810                     this.expand();
38811                 }
38812             }else{
38813                 this.selectedIndex = -1;
38814                 this.onLoad();   
38815             }
38816         }
38817     },
38818
38819     // private
38820     getParams : function(q){
38821         var p = {};
38822         //p[this.queryParam] = q;
38823         if(this.pageSize){
38824             p.start = 0;
38825             p.limit = this.pageSize;
38826         }
38827         return p;
38828     },
38829
38830     /**
38831      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38832      */
38833     collapse : function(){
38834         if(!this.isExpanded()){
38835             return;
38836         }
38837         this.list.hide();
38838         Roo.get(document).un('mousedown', this.collapseIf, this);
38839         Roo.get(document).un('mousewheel', this.collapseIf, this);
38840         if (!this.editable) {
38841             Roo.get(document).un('keydown', this.listKeyPress, this);
38842         }
38843         this.fireEvent('collapse', this);
38844     },
38845
38846     // private
38847     collapseIf : function(e){
38848         if(!e.within(this.wrap) && !e.within(this.list)){
38849             this.collapse();
38850         }
38851     },
38852
38853     /**
38854      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38855      */
38856     expand : function(){
38857         if(this.isExpanded() || !this.hasFocus){
38858             return;
38859         }
38860         this.list.alignTo(this.el, this.listAlign);
38861         this.list.show();
38862         Roo.get(document).on('mousedown', this.collapseIf, this);
38863         Roo.get(document).on('mousewheel', this.collapseIf, this);
38864         if (!this.editable) {
38865             Roo.get(document).on('keydown', this.listKeyPress, this);
38866         }
38867         
38868         this.fireEvent('expand', this);
38869     },
38870
38871     // private
38872     // Implements the default empty TriggerField.onTriggerClick function
38873     onTriggerClick : function(){
38874         if(this.disabled){
38875             return;
38876         }
38877         if(this.isExpanded()){
38878             this.collapse();
38879             if (!this.blockFocus) {
38880                 this.el.focus();
38881             }
38882             
38883         }else {
38884             this.hasFocus = true;
38885             if(this.triggerAction == 'all') {
38886                 this.doQuery(this.allQuery, true);
38887             } else {
38888                 this.doQuery(this.getRawValue());
38889             }
38890             if (!this.blockFocus) {
38891                 this.el.focus();
38892             }
38893         }
38894     },
38895     listKeyPress : function(e)
38896     {
38897         //Roo.log('listkeypress');
38898         // scroll to first matching element based on key pres..
38899         if (e.isSpecialKey()) {
38900             return false;
38901         }
38902         var k = String.fromCharCode(e.getKey()).toUpperCase();
38903         //Roo.log(k);
38904         var match  = false;
38905         var csel = this.view.getSelectedNodes();
38906         var cselitem = false;
38907         if (csel.length) {
38908             var ix = this.view.indexOf(csel[0]);
38909             cselitem  = this.store.getAt(ix);
38910             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38911                 cselitem = false;
38912             }
38913             
38914         }
38915         
38916         this.store.each(function(v) { 
38917             if (cselitem) {
38918                 // start at existing selection.
38919                 if (cselitem.id == v.id) {
38920                     cselitem = false;
38921                 }
38922                 return;
38923             }
38924                 
38925             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38926                 match = this.store.indexOf(v);
38927                 return false;
38928             }
38929         }, this);
38930         
38931         if (match === false) {
38932             return true; // no more action?
38933         }
38934         // scroll to?
38935         this.view.select(match);
38936         var sn = Roo.get(this.view.getSelectedNodes()[0])
38937         sn.scrollIntoView(sn.dom.parentNode, false);
38938     }
38939
38940     /** 
38941     * @cfg {Boolean} grow 
38942     * @hide 
38943     */
38944     /** 
38945     * @cfg {Number} growMin 
38946     * @hide 
38947     */
38948     /** 
38949     * @cfg {Number} growMax 
38950     * @hide 
38951     */
38952     /**
38953      * @hide
38954      * @method autoSize
38955      */
38956 });/*
38957  * Copyright(c) 2010-2012, Roo J Solutions Limited
38958  *
38959  * Licence LGPL
38960  *
38961  */
38962
38963 /**
38964  * @class Roo.form.ComboBoxArray
38965  * @extends Roo.form.TextField
38966  * A facebook style adder... for lists of email / people / countries  etc...
38967  * pick multiple items from a combo box, and shows each one.
38968  *
38969  *  Fred [x]  Brian [x]  [Pick another |v]
38970  *
38971  *
38972  *  For this to work: it needs various extra information
38973  *    - normal combo problay has
38974  *      name, hiddenName
38975  *    + displayField, valueField
38976  *
38977  *    For our purpose...
38978  *
38979  *
38980  *   If we change from 'extends' to wrapping...
38981  *   
38982  *  
38983  *
38984  
38985  
38986  * @constructor
38987  * Create a new ComboBoxArray.
38988  * @param {Object} config Configuration options
38989  */
38990  
38991
38992 Roo.form.ComboBoxArray = function(config)
38993 {
38994     
38995     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
38996     
38997     this.items = new Roo.util.MixedCollection(false);
38998     
38999     // construct the child combo...
39000     
39001     
39002     
39003     
39004    
39005     
39006 }
39007
39008  
39009 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
39010
39011     /**
39012      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
39013      */
39014     
39015     lastData : false,
39016     
39017     // behavies liek a hiddne field
39018     inputType:      'hidden',
39019     /**
39020      * @cfg {Number} width The width of the box that displays the selected element
39021      */ 
39022     width:          300,
39023
39024     
39025     
39026     /**
39027      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
39028      */
39029     name : false,
39030     /**
39031      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
39032      */
39033     hiddenName : false,
39034     
39035     
39036     // private the array of items that are displayed..
39037     items  : false,
39038     // private - the hidden field el.
39039     hiddenEl : false,
39040     // private - the filed el..
39041     el : false,
39042     
39043     //validateValue : function() { return true; }, // all values are ok!
39044     //onAddClick: function() { },
39045     
39046     onRender : function(ct, position) 
39047     {
39048         
39049         // create the standard hidden element
39050         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
39051         
39052         
39053         // give fake names to child combo;
39054         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
39055         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
39056         
39057         this.combo = Roo.factory(this.combo, Roo.form);
39058         this.combo.onRender(ct, position);
39059         
39060         // assigned so form know we need to do this..
39061         this.store          = this.combo.store;
39062         this.valueField     = this.combo.valueField;
39063         this.displayField   = this.combo.displayField ;
39064         
39065         
39066         this.combo.wrap.addClass('x-cbarray-grp');
39067         
39068         var cbwrap = this.combo.wrap.createChild(
39069             {tag: 'div', cls: 'x-cbarray-cb'},
39070             this.combo.el.dom
39071         );
39072         
39073              
39074         this.hiddenEl = this.combo.wrap.createChild({
39075             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
39076         });
39077         this.el = this.combo.wrap.createChild({
39078             tag: 'input',  type:'hidden' , name: this.name, value : ''
39079         });
39080          //   this.el.dom.removeAttribute("name");
39081         
39082         
39083         this.outerWrap = this.combo.wrap;
39084         this.wrap = cbwrap;
39085         
39086         this.outerWrap.setWidth(this.width);
39087         this.outerWrap.dom.removeChild(this.el.dom);
39088         
39089         this.wrap.dom.appendChild(this.el.dom);
39090         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
39091         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
39092         
39093         this.combo.trigger.setStyle('position','relative');
39094         this.combo.trigger.setStyle('left', '0px');
39095         this.combo.trigger.setStyle('top', '2px');
39096         
39097         this.combo.el.setStyle('vertical-align', 'text-bottom');
39098         
39099         //this.trigger.setStyle('vertical-align', 'top');
39100         
39101         // this should use the code from combo really... on('add' ....)
39102         if (this.adder) {
39103             
39104         
39105             this.adder = this.outerWrap.createChild(
39106                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
39107             var _t = this;
39108             this.adder.on('click', function(e) {
39109                 _t.fireEvent('adderclick', this, e);
39110             }, _t);
39111         }
39112         //var _t = this;
39113         //this.adder.on('click', this.onAddClick, _t);
39114         
39115         
39116         this.combo.on('select', function(cb, rec, ix) {
39117             this.addItem(rec.data);
39118             
39119             cb.setValue('');
39120             cb.el.dom.value = '';
39121             //cb.lastData = rec.data;
39122             // add to list
39123             
39124         }, this);
39125         
39126         
39127     },
39128     
39129     
39130     getName: function()
39131     {
39132         // returns hidden if it's set..
39133         if (!this.rendered) {return ''};
39134         return  this.hiddenName ? this.hiddenName : this.name;
39135         
39136     },
39137     
39138     
39139     onResize: function(w, h){
39140         
39141         return;
39142         // not sure if this is needed..
39143         //this.combo.onResize(w,h);
39144         
39145         if(typeof w != 'number'){
39146             // we do not handle it!?!?
39147             return;
39148         }
39149         var tw = this.combo.trigger.getWidth();
39150         tw += this.addicon ? this.addicon.getWidth() : 0;
39151         tw += this.editicon ? this.editicon.getWidth() : 0;
39152         var x = w - tw;
39153         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
39154             
39155         this.combo.trigger.setStyle('left', '0px');
39156         
39157         if(this.list && this.listWidth === undefined){
39158             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
39159             this.list.setWidth(lw);
39160             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39161         }
39162         
39163     
39164         
39165     },
39166     
39167     addItem: function(rec)
39168     {
39169         var valueField = this.combo.valueField;
39170         var displayField = this.combo.displayField;
39171         if (this.items.indexOfKey(rec[valueField]) > -1) {
39172             //console.log("GOT " + rec.data.id);
39173             return;
39174         }
39175         
39176         var x = new Roo.form.ComboBoxArray.Item({
39177             //id : rec[this.idField],
39178             data : rec,
39179             displayField : displayField ,
39180             tipField : displayField ,
39181             cb : this
39182         });
39183         // use the 
39184         this.items.add(rec[valueField],x);
39185         // add it before the element..
39186         this.updateHiddenEl();
39187         x.render(this.outerWrap, this.wrap.dom);
39188         // add the image handler..
39189     },
39190     
39191     updateHiddenEl : function()
39192     {
39193         this.validate();
39194         if (!this.hiddenEl) {
39195             return;
39196         }
39197         var ar = [];
39198         var idField = this.combo.valueField;
39199         
39200         this.items.each(function(f) {
39201             ar.push(f.data[idField]);
39202            
39203         });
39204         this.hiddenEl.dom.value = ar.join(',');
39205         this.validate();
39206     },
39207     
39208     reset : function()
39209     {
39210         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
39211         this.items.each(function(f) {
39212            f.remove(); 
39213         });
39214         this.el.dom.value = '';
39215         if (this.hiddenEl) {
39216             this.hiddenEl.dom.value = '';
39217         }
39218         
39219     },
39220     getValue: function()
39221     {
39222         return this.hiddenEl ? this.hiddenEl.dom.value : '';
39223     },
39224     setValue: function(v) // not a valid action - must use addItems..
39225     {
39226          
39227         this.reset();
39228         
39229         
39230         
39231         if (this.store.isLocal && (typeof(v) == 'string')) {
39232             // then we can use the store to find the values..
39233             // comma seperated at present.. this needs to allow JSON based encoding..
39234             this.hiddenEl.value  = v;
39235             var v_ar = [];
39236             Roo.each(v.split(','), function(k) {
39237                 Roo.log("CHECK " + this.valueField + ',' + k);
39238                 var li = this.store.query(this.valueField, k);
39239                 if (!li.length) {
39240                     return;
39241                 }
39242                 add = {};
39243                 add[this.valueField] = k;
39244                 add[this.displayField] = li.item(0).data[this.displayField];
39245                 
39246                 this.addItem(add);
39247             }, this) 
39248              
39249         }
39250         if (typeof(v) == 'object') {
39251             // then let's assume it's an array of objects..
39252             Roo.each(v, function(l) {
39253                 this.addItem(l);
39254             }, this);
39255              
39256         }
39257         
39258         
39259     },
39260     setFromData: function(v)
39261     {
39262         // this recieves an object, if setValues is called.
39263         this.reset();
39264         this.el.dom.value = v[this.displayField];
39265         this.hiddenEl.dom.value = v[this.valueField];
39266         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
39267             return;
39268         }
39269         var kv = v[this.valueField];
39270         var dv = v[this.displayField];
39271         kv = typeof(kv) != 'string' ? '' : kv;
39272         dv = typeof(dv) != 'string' ? '' : dv;
39273         
39274         
39275         var keys = kv.split(',');
39276         var display = dv.split(',');
39277         for (var i = 0 ; i < keys.length; i++) {
39278             
39279             add = {};
39280             add[this.valueField] = keys[i];
39281             add[this.displayField] = display[i];
39282             this.addItem(add);
39283         }
39284       
39285         
39286     },
39287     
39288     
39289     validateValue : function(value){
39290         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
39291         
39292     }
39293     
39294 });
39295
39296
39297
39298 /**
39299  * @class Roo.form.ComboBoxArray.Item
39300  * @extends Roo.BoxComponent
39301  * A selected item in the list
39302  *  Fred [x]  Brian [x]  [Pick another |v]
39303  * 
39304  * @constructor
39305  * Create a new item.
39306  * @param {Object} config Configuration options
39307  */
39308  
39309 Roo.form.ComboBoxArray.Item = function(config) {
39310     config.id = Roo.id();
39311     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
39312 }
39313
39314 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
39315     data : {},
39316     cb: false,
39317     displayField : false,
39318     tipField : false,
39319     
39320     
39321     defaultAutoCreate : {
39322         tag: 'div',
39323         cls: 'x-cbarray-item',
39324         cn : [ 
39325             { tag: 'div' },
39326             {
39327                 tag: 'img',
39328                 width:16,
39329                 height : 16,
39330                 src : Roo.BLANK_IMAGE_URL ,
39331                 align: 'center'
39332             }
39333         ]
39334         
39335     },
39336     
39337  
39338     onRender : function(ct, position)
39339     {
39340         Roo.form.Field.superclass.onRender.call(this, ct, position);
39341         
39342         if(!this.el){
39343             var cfg = this.getAutoCreate();
39344             this.el = ct.createChild(cfg, position);
39345         }
39346         
39347         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
39348         
39349         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
39350             this.cb.renderer(this.data) :
39351             String.format('{0}',this.data[this.displayField]);
39352         
39353             
39354         this.el.child('div').dom.setAttribute('qtip',
39355                         String.format('{0}',this.data[this.tipField])
39356         );
39357         
39358         this.el.child('img').on('click', this.remove, this);
39359         
39360     },
39361    
39362     remove : function()
39363     {
39364         
39365         this.cb.items.remove(this);
39366         this.el.child('img').un('click', this.remove, this);
39367         this.el.remove();
39368         this.cb.updateHiddenEl();
39369     }
39370     
39371     
39372 });/*
39373  * Based on:
39374  * Ext JS Library 1.1.1
39375  * Copyright(c) 2006-2007, Ext JS, LLC.
39376  *
39377  * Originally Released Under LGPL - original licence link has changed is not relivant.
39378  *
39379  * Fork - LGPL
39380  * <script type="text/javascript">
39381  */
39382 /**
39383  * @class Roo.form.Checkbox
39384  * @extends Roo.form.Field
39385  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
39386  * @constructor
39387  * Creates a new Checkbox
39388  * @param {Object} config Configuration options
39389  */
39390 Roo.form.Checkbox = function(config){
39391     Roo.form.Checkbox.superclass.constructor.call(this, config);
39392     this.addEvents({
39393         /**
39394          * @event check
39395          * Fires when the checkbox is checked or unchecked.
39396              * @param {Roo.form.Checkbox} this This checkbox
39397              * @param {Boolean} checked The new checked value
39398              */
39399         check : true
39400     });
39401 };
39402
39403 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
39404     /**
39405      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
39406      */
39407     focusClass : undefined,
39408     /**
39409      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
39410      */
39411     fieldClass: "x-form-field",
39412     /**
39413      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
39414      */
39415     checked: false,
39416     /**
39417      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39418      * {tag: "input", type: "checkbox", autocomplete: "off"})
39419      */
39420     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
39421     /**
39422      * @cfg {String} boxLabel The text that appears beside the checkbox
39423      */
39424     boxLabel : "",
39425     /**
39426      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
39427      */  
39428     inputValue : '1',
39429     /**
39430      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
39431      */
39432      valueOff: '0', // value when not checked..
39433
39434     actionMode : 'viewEl', 
39435     //
39436     // private
39437     itemCls : 'x-menu-check-item x-form-item',
39438     groupClass : 'x-menu-group-item',
39439     inputType : 'hidden',
39440     
39441     
39442     inSetChecked: false, // check that we are not calling self...
39443     
39444     inputElement: false, // real input element?
39445     basedOn: false, // ????
39446     
39447     isFormField: true, // not sure where this is needed!!!!
39448
39449     onResize : function(){
39450         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
39451         if(!this.boxLabel){
39452             this.el.alignTo(this.wrap, 'c-c');
39453         }
39454     },
39455
39456     initEvents : function(){
39457         Roo.form.Checkbox.superclass.initEvents.call(this);
39458         this.el.on("click", this.onClick,  this);
39459         this.el.on("change", this.onClick,  this);
39460     },
39461
39462
39463     getResizeEl : function(){
39464         return this.wrap;
39465     },
39466
39467     getPositionEl : function(){
39468         return this.wrap;
39469     },
39470
39471     // private
39472     onRender : function(ct, position){
39473         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
39474         /*
39475         if(this.inputValue !== undefined){
39476             this.el.dom.value = this.inputValue;
39477         }
39478         */
39479         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
39480         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
39481         var viewEl = this.wrap.createChild({ 
39482             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
39483         this.viewEl = viewEl;   
39484         this.wrap.on('click', this.onClick,  this); 
39485         
39486         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
39487         this.el.on('propertychange', this.setFromHidden,  this);  //ie
39488         
39489         
39490         
39491         if(this.boxLabel){
39492             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
39493         //    viewEl.on('click', this.onClick,  this); 
39494         }
39495         //if(this.checked){
39496             this.setChecked(this.checked);
39497         //}else{
39498             //this.checked = this.el.dom;
39499         //}
39500
39501     },
39502
39503     // private
39504     initValue : Roo.emptyFn,
39505
39506     /**
39507      * Returns the checked state of the checkbox.
39508      * @return {Boolean} True if checked, else false
39509      */
39510     getValue : function(){
39511         if(this.el){
39512             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
39513         }
39514         return this.valueOff;
39515         
39516     },
39517
39518         // private
39519     onClick : function(){ 
39520         this.setChecked(!this.checked);
39521
39522         //if(this.el.dom.checked != this.checked){
39523         //    this.setValue(this.el.dom.checked);
39524        // }
39525     },
39526
39527     /**
39528      * Sets the checked state of the checkbox.
39529      * On is always based on a string comparison between inputValue and the param.
39530      * @param {Boolean/String} value - the value to set 
39531      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
39532      */
39533     setValue : function(v,suppressEvent){
39534         
39535         
39536         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
39537         //if(this.el && this.el.dom){
39538         //    this.el.dom.checked = this.checked;
39539         //    this.el.dom.defaultChecked = this.checked;
39540         //}
39541         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
39542         //this.fireEvent("check", this, this.checked);
39543     },
39544     // private..
39545     setChecked : function(state,suppressEvent)
39546     {
39547         if (this.inSetChecked) {
39548             this.checked = state;
39549             return;
39550         }
39551         
39552     
39553         if(this.wrap){
39554             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
39555         }
39556         this.checked = state;
39557         if(suppressEvent !== true){
39558             this.fireEvent('check', this, state);
39559         }
39560         this.inSetChecked = true;
39561         this.el.dom.value = state ? this.inputValue : this.valueOff;
39562         this.inSetChecked = false;
39563         
39564     },
39565     // handle setting of hidden value by some other method!!?!?
39566     setFromHidden: function()
39567     {
39568         if(!this.el){
39569             return;
39570         }
39571         //console.log("SET FROM HIDDEN");
39572         //alert('setFrom hidden');
39573         this.setValue(this.el.dom.value);
39574     },
39575     
39576     onDestroy : function()
39577     {
39578         if(this.viewEl){
39579             Roo.get(this.viewEl).remove();
39580         }
39581          
39582         Roo.form.Checkbox.superclass.onDestroy.call(this);
39583     }
39584
39585 });/*
39586  * Based on:
39587  * Ext JS Library 1.1.1
39588  * Copyright(c) 2006-2007, Ext JS, LLC.
39589  *
39590  * Originally Released Under LGPL - original licence link has changed is not relivant.
39591  *
39592  * Fork - LGPL
39593  * <script type="text/javascript">
39594  */
39595  
39596 /**
39597  * @class Roo.form.Radio
39598  * @extends Roo.form.Checkbox
39599  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
39600  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
39601  * @constructor
39602  * Creates a new Radio
39603  * @param {Object} config Configuration options
39604  */
39605 Roo.form.Radio = function(){
39606     Roo.form.Radio.superclass.constructor.apply(this, arguments);
39607 };
39608 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
39609     inputType: 'radio',
39610
39611     /**
39612      * If this radio is part of a group, it will return the selected value
39613      * @return {String}
39614      */
39615     getGroupValue : function(){
39616         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
39617     }
39618 });//<script type="text/javascript">
39619
39620 /*
39621  * Ext JS Library 1.1.1
39622  * Copyright(c) 2006-2007, Ext JS, LLC.
39623  * licensing@extjs.com
39624  * 
39625  * http://www.extjs.com/license
39626  */
39627  
39628  /*
39629   * 
39630   * Known bugs:
39631   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
39632   * - IE ? - no idea how much works there.
39633   * 
39634   * 
39635   * 
39636   */
39637  
39638
39639 /**
39640  * @class Ext.form.HtmlEditor
39641  * @extends Ext.form.Field
39642  * Provides a lightweight HTML Editor component.
39643  *
39644  * This has been tested on Fireforx / Chrome.. IE may not be so great..
39645  * 
39646  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
39647  * supported by this editor.</b><br/><br/>
39648  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
39649  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
39650  */
39651 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
39652       /**
39653      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
39654      */
39655     toolbars : false,
39656     /**
39657      * @cfg {String} createLinkText The default text for the create link prompt
39658      */
39659     createLinkText : 'Please enter the URL for the link:',
39660     /**
39661      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
39662      */
39663     defaultLinkValue : 'http:/'+'/',
39664    
39665      /**
39666      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
39667      *                        Roo.resizable.
39668      */
39669     resizable : false,
39670      /**
39671      * @cfg {Number} height (in pixels)
39672      */   
39673     height: 300,
39674    /**
39675      * @cfg {Number} width (in pixels)
39676      */   
39677     width: 500,
39678     
39679     /**
39680      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
39681      * 
39682      */
39683     stylesheets: false,
39684     
39685     // id of frame..
39686     frameId: false,
39687     
39688     // private properties
39689     validationEvent : false,
39690     deferHeight: true,
39691     initialized : false,
39692     activated : false,
39693     sourceEditMode : false,
39694     onFocus : Roo.emptyFn,
39695     iframePad:3,
39696     hideMode:'offsets',
39697     
39698     defaultAutoCreate : { // modified by initCompnoent..
39699         tag: "textarea",
39700         style:"width:500px;height:300px;",
39701         autocomplete: "off"
39702     },
39703
39704     // private
39705     initComponent : function(){
39706         this.addEvents({
39707             /**
39708              * @event initialize
39709              * Fires when the editor is fully initialized (including the iframe)
39710              * @param {HtmlEditor} this
39711              */
39712             initialize: true,
39713             /**
39714              * @event activate
39715              * Fires when the editor is first receives the focus. Any insertion must wait
39716              * until after this event.
39717              * @param {HtmlEditor} this
39718              */
39719             activate: true,
39720              /**
39721              * @event beforesync
39722              * Fires before the textarea is updated with content from the editor iframe. Return false
39723              * to cancel the sync.
39724              * @param {HtmlEditor} this
39725              * @param {String} html
39726              */
39727             beforesync: true,
39728              /**
39729              * @event beforepush
39730              * Fires before the iframe editor is updated with content from the textarea. Return false
39731              * to cancel the push.
39732              * @param {HtmlEditor} this
39733              * @param {String} html
39734              */
39735             beforepush: true,
39736              /**
39737              * @event sync
39738              * Fires when the textarea is updated with content from the editor iframe.
39739              * @param {HtmlEditor} this
39740              * @param {String} html
39741              */
39742             sync: true,
39743              /**
39744              * @event push
39745              * Fires when the iframe editor is updated with content from the textarea.
39746              * @param {HtmlEditor} this
39747              * @param {String} html
39748              */
39749             push: true,
39750              /**
39751              * @event editmodechange
39752              * Fires when the editor switches edit modes
39753              * @param {HtmlEditor} this
39754              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
39755              */
39756             editmodechange: true,
39757             /**
39758              * @event editorevent
39759              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
39760              * @param {HtmlEditor} this
39761              */
39762             editorevent: true
39763         });
39764         this.defaultAutoCreate =  {
39765             tag: "textarea",
39766             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
39767             autocomplete: "off"
39768         };
39769     },
39770
39771     /**
39772      * Protected method that will not generally be called directly. It
39773      * is called when the editor creates its toolbar. Override this method if you need to
39774      * add custom toolbar buttons.
39775      * @param {HtmlEditor} editor
39776      */
39777     createToolbar : function(editor){
39778         if (!editor.toolbars || !editor.toolbars.length) {
39779             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
39780         }
39781         
39782         for (var i =0 ; i < editor.toolbars.length;i++) {
39783             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
39784             editor.toolbars[i].init(editor);
39785         }
39786          
39787         
39788     },
39789
39790     /**
39791      * Protected method that will not generally be called directly. It
39792      * is called when the editor initializes the iframe with HTML contents. Override this method if you
39793      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
39794      */
39795     getDocMarkup : function(){
39796         // body styles..
39797         var st = '';
39798         if (this.stylesheets === false) {
39799             
39800             Roo.get(document.head).select('style').each(function(node) {
39801                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
39802             });
39803             
39804             Roo.get(document.head).select('link').each(function(node) { 
39805                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
39806             });
39807             
39808         } else if (!this.stylesheets.length) {
39809                 // simple..
39810                 st = '<style type="text/css">' +
39811                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
39812                    '</style>';
39813         } else {
39814             Roo.each(this.stylesheets, function(s) {
39815                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
39816             });
39817             
39818         }
39819         
39820         st +=  '<style type="text/css">' +
39821             'IMG { cursor: pointer } ' +
39822         '</style>';
39823
39824         
39825         return '<html><head>' + st  +
39826             //<style type="text/css">' +
39827             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
39828             //'</style>' +
39829             ' </head><body class="roo-htmleditor-body"></body></html>';
39830     },
39831
39832     // private
39833     onRender : function(ct, position)
39834     {
39835         var _t = this;
39836         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
39837         this.el.dom.style.border = '0 none';
39838         this.el.dom.setAttribute('tabIndex', -1);
39839         this.el.addClass('x-hidden');
39840         if(Roo.isIE){ // fix IE 1px bogus margin
39841             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
39842         }
39843         this.wrap = this.el.wrap({
39844             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
39845         });
39846         
39847         if (this.resizable) {
39848             this.resizeEl = new Roo.Resizable(this.wrap, {
39849                 pinned : true,
39850                 wrap: true,
39851                 dynamic : true,
39852                 minHeight : this.height,
39853                 height: this.height,
39854                 handles : this.resizable,
39855                 width: this.width,
39856                 listeners : {
39857                     resize : function(r, w, h) {
39858                         _t.onResize(w,h); // -something
39859                     }
39860                 }
39861             });
39862             
39863         }
39864
39865         this.frameId = Roo.id();
39866         
39867         this.createToolbar(this);
39868         
39869       
39870         
39871         var iframe = this.wrap.createChild({
39872             tag: 'iframe',
39873             id: this.frameId,
39874             name: this.frameId,
39875             frameBorder : 'no',
39876             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
39877         }, this.el
39878         );
39879         
39880        // console.log(iframe);
39881         //this.wrap.dom.appendChild(iframe);
39882
39883         this.iframe = iframe.dom;
39884
39885          this.assignDocWin();
39886         
39887         this.doc.designMode = 'on';
39888        
39889         this.doc.open();
39890         this.doc.write(this.getDocMarkup());
39891         this.doc.close();
39892
39893         
39894         var task = { // must defer to wait for browser to be ready
39895             run : function(){
39896                 //console.log("run task?" + this.doc.readyState);
39897                 this.assignDocWin();
39898                 if(this.doc.body || this.doc.readyState == 'complete'){
39899                     try {
39900                         this.doc.designMode="on";
39901                     } catch (e) {
39902                         return;
39903                     }
39904                     Roo.TaskMgr.stop(task);
39905                     this.initEditor.defer(10, this);
39906                 }
39907             },
39908             interval : 10,
39909             duration:10000,
39910             scope: this
39911         };
39912         Roo.TaskMgr.start(task);
39913
39914         if(!this.width){
39915             this.setSize(this.wrap.getSize());
39916         }
39917         if (this.resizeEl) {
39918             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
39919             // should trigger onReize..
39920         }
39921     },
39922
39923     // private
39924     onResize : function(w, h)
39925     {
39926         //Roo.log('resize: ' +w + ',' + h );
39927         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
39928         if(this.el && this.iframe){
39929             if(typeof w == 'number'){
39930                 var aw = w - this.wrap.getFrameWidth('lr');
39931                 this.el.setWidth(this.adjustWidth('textarea', aw));
39932                 this.iframe.style.width = aw + 'px';
39933             }
39934             if(typeof h == 'number'){
39935                 var tbh = 0;
39936                 for (var i =0; i < this.toolbars.length;i++) {
39937                     // fixme - ask toolbars for heights?
39938                     tbh += this.toolbars[i].tb.el.getHeight();
39939                     if (this.toolbars[i].footer) {
39940                         tbh += this.toolbars[i].footer.el.getHeight();
39941                     }
39942                 }
39943                 
39944                 
39945                 
39946                 
39947                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
39948                 ah -= 5; // knock a few pixes off for look..
39949                 this.el.setHeight(this.adjustWidth('textarea', ah));
39950                 this.iframe.style.height = ah + 'px';
39951                 if(this.doc){
39952                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
39953                 }
39954             }
39955         }
39956     },
39957
39958     /**
39959      * Toggles the editor between standard and source edit mode.
39960      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
39961      */
39962     toggleSourceEdit : function(sourceEditMode){
39963         
39964         this.sourceEditMode = sourceEditMode === true;
39965         
39966         if(this.sourceEditMode){
39967           
39968             this.syncValue();
39969             this.iframe.className = 'x-hidden';
39970             this.el.removeClass('x-hidden');
39971             this.el.dom.removeAttribute('tabIndex');
39972             this.el.focus();
39973         }else{
39974              
39975             this.pushValue();
39976             this.iframe.className = '';
39977             this.el.addClass('x-hidden');
39978             this.el.dom.setAttribute('tabIndex', -1);
39979             this.deferFocus();
39980         }
39981         this.setSize(this.wrap.getSize());
39982         this.fireEvent('editmodechange', this, this.sourceEditMode);
39983     },
39984
39985     // private used internally
39986     createLink : function(){
39987         var url = prompt(this.createLinkText, this.defaultLinkValue);
39988         if(url && url != 'http:/'+'/'){
39989             this.relayCmd('createlink', url);
39990         }
39991     },
39992
39993     // private (for BoxComponent)
39994     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39995
39996     // private (for BoxComponent)
39997     getResizeEl : function(){
39998         return this.wrap;
39999     },
40000
40001     // private (for BoxComponent)
40002     getPositionEl : function(){
40003         return this.wrap;
40004     },
40005
40006     // private
40007     initEvents : function(){
40008         this.originalValue = this.getValue();
40009     },
40010
40011     /**
40012      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40013      * @method
40014      */
40015     markInvalid : Roo.emptyFn,
40016     /**
40017      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40018      * @method
40019      */
40020     clearInvalid : Roo.emptyFn,
40021
40022     setValue : function(v){
40023         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
40024         this.pushValue();
40025     },
40026
40027     /**
40028      * Protected method that will not generally be called directly. If you need/want
40029      * custom HTML cleanup, this is the method you should override.
40030      * @param {String} html The HTML to be cleaned
40031      * return {String} The cleaned HTML
40032      */
40033     cleanHtml : function(html){
40034         html = String(html);
40035         if(html.length > 5){
40036             if(Roo.isSafari){ // strip safari nonsense
40037                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
40038             }
40039         }
40040         if(html == '&nbsp;'){
40041             html = '';
40042         }
40043         return html;
40044     },
40045
40046     /**
40047      * Protected method that will not generally be called directly. Syncs the contents
40048      * of the editor iframe with the textarea.
40049      */
40050     syncValue : function(){
40051         if(this.initialized){
40052             var bd = (this.doc.body || this.doc.documentElement);
40053             //this.cleanUpPaste(); -- this is done else where and causes havoc..
40054             var html = bd.innerHTML;
40055             if(Roo.isSafari){
40056                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
40057                 var m = bs.match(/text-align:(.*?);/i);
40058                 if(m && m[1]){
40059                     html = '<div style="'+m[0]+'">' + html + '</div>';
40060                 }
40061             }
40062             html = this.cleanHtml(html);
40063             // fix up the special chars..
40064             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
40065                 return "&#"+b.charCodeAt()+";" 
40066             });
40067             if(this.fireEvent('beforesync', this, html) !== false){
40068                 this.el.dom.value = html;
40069                 this.fireEvent('sync', this, html);
40070             }
40071         }
40072     },
40073
40074     /**
40075      * Protected method that will not generally be called directly. Pushes the value of the textarea
40076      * into the iframe editor.
40077      */
40078     pushValue : function(){
40079         if(this.initialized){
40080             var v = this.el.dom.value;
40081             if(v.length < 1){
40082                 v = '&#160;';
40083             }
40084             
40085             if(this.fireEvent('beforepush', this, v) !== false){
40086                 var d = (this.doc.body || this.doc.documentElement);
40087                 d.innerHTML = v;
40088                 this.cleanUpPaste();
40089                 this.el.dom.value = d.innerHTML;
40090                 this.fireEvent('push', this, v);
40091             }
40092         }
40093     },
40094
40095     // private
40096     deferFocus : function(){
40097         this.focus.defer(10, this);
40098     },
40099
40100     // doc'ed in Field
40101     focus : function(){
40102         if(this.win && !this.sourceEditMode){
40103             this.win.focus();
40104         }else{
40105             this.el.focus();
40106         }
40107     },
40108     
40109     assignDocWin: function()
40110     {
40111         var iframe = this.iframe;
40112         
40113          if(Roo.isIE){
40114             this.doc = iframe.contentWindow.document;
40115             this.win = iframe.contentWindow;
40116         } else {
40117             if (!Roo.get(this.frameId)) {
40118                 return;
40119             }
40120             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
40121             this.win = Roo.get(this.frameId).dom.contentWindow;
40122         }
40123     },
40124     
40125     // private
40126     initEditor : function(){
40127         //console.log("INIT EDITOR");
40128         this.assignDocWin();
40129         
40130         
40131         
40132         this.doc.designMode="on";
40133         this.doc.open();
40134         this.doc.write(this.getDocMarkup());
40135         this.doc.close();
40136         
40137         var dbody = (this.doc.body || this.doc.documentElement);
40138         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
40139         // this copies styles from the containing element into thsi one..
40140         // not sure why we need all of this..
40141         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
40142         ss['background-attachment'] = 'fixed'; // w3c
40143         dbody.bgProperties = 'fixed'; // ie
40144         Roo.DomHelper.applyStyles(dbody, ss);
40145         Roo.EventManager.on(this.doc, {
40146             //'mousedown': this.onEditorEvent,
40147             'mouseup': this.onEditorEvent,
40148             'dblclick': this.onEditorEvent,
40149             'click': this.onEditorEvent,
40150             'keyup': this.onEditorEvent,
40151             buffer:100,
40152             scope: this
40153         });
40154         if(Roo.isGecko){
40155             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
40156         }
40157         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
40158             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
40159         }
40160         this.initialized = true;
40161
40162         this.fireEvent('initialize', this);
40163         this.pushValue();
40164     },
40165
40166     // private
40167     onDestroy : function(){
40168         
40169         
40170         
40171         if(this.rendered){
40172             
40173             for (var i =0; i < this.toolbars.length;i++) {
40174                 // fixme - ask toolbars for heights?
40175                 this.toolbars[i].onDestroy();
40176             }
40177             
40178             this.wrap.dom.innerHTML = '';
40179             this.wrap.remove();
40180         }
40181     },
40182
40183     // private
40184     onFirstFocus : function(){
40185         
40186         this.assignDocWin();
40187         
40188         
40189         this.activated = true;
40190         for (var i =0; i < this.toolbars.length;i++) {
40191             this.toolbars[i].onFirstFocus();
40192         }
40193        
40194         if(Roo.isGecko){ // prevent silly gecko errors
40195             this.win.focus();
40196             var s = this.win.getSelection();
40197             if(!s.focusNode || s.focusNode.nodeType != 3){
40198                 var r = s.getRangeAt(0);
40199                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
40200                 r.collapse(true);
40201                 this.deferFocus();
40202             }
40203             try{
40204                 this.execCmd('useCSS', true);
40205                 this.execCmd('styleWithCSS', false);
40206             }catch(e){}
40207         }
40208         this.fireEvent('activate', this);
40209     },
40210
40211     // private
40212     adjustFont: function(btn){
40213         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
40214         //if(Roo.isSafari){ // safari
40215         //    adjust *= 2;
40216        // }
40217         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
40218         if(Roo.isSafari){ // safari
40219             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
40220             v =  (v < 10) ? 10 : v;
40221             v =  (v > 48) ? 48 : v;
40222             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
40223             
40224         }
40225         
40226         
40227         v = Math.max(1, v+adjust);
40228         
40229         this.execCmd('FontSize', v  );
40230     },
40231
40232     onEditorEvent : function(e){
40233         this.fireEvent('editorevent', this, e);
40234       //  this.updateToolbar();
40235         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
40236     },
40237
40238     insertTag : function(tg)
40239     {
40240         // could be a bit smarter... -> wrap the current selected tRoo..
40241         
40242         this.execCmd("formatblock",   tg);
40243         
40244     },
40245     
40246     insertText : function(txt)
40247     {
40248         
40249         
40250         range = this.createRange();
40251         range.deleteContents();
40252                //alert(Sender.getAttribute('label'));
40253                
40254         range.insertNode(this.doc.createTextNode(txt));
40255     } ,
40256     
40257     // private
40258     relayBtnCmd : function(btn){
40259         this.relayCmd(btn.cmd);
40260     },
40261
40262     /**
40263      * Executes a Midas editor command on the editor document and performs necessary focus and
40264      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
40265      * @param {String} cmd The Midas command
40266      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40267      */
40268     relayCmd : function(cmd, value){
40269         this.win.focus();
40270         this.execCmd(cmd, value);
40271         this.fireEvent('editorevent', this);
40272         //this.updateToolbar();
40273         this.deferFocus();
40274     },
40275
40276     /**
40277      * Executes a Midas editor command directly on the editor document.
40278      * For visual commands, you should use {@link #relayCmd} instead.
40279      * <b>This should only be called after the editor is initialized.</b>
40280      * @param {String} cmd The Midas command
40281      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40282      */
40283     execCmd : function(cmd, value){
40284         this.doc.execCommand(cmd, false, value === undefined ? null : value);
40285         this.syncValue();
40286     },
40287  
40288  
40289    
40290     /**
40291      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
40292      * to insert tRoo.
40293      * @param {String} text | dom node.. 
40294      */
40295     insertAtCursor : function(text)
40296     {
40297         
40298         
40299         
40300         if(!this.activated){
40301             return;
40302         }
40303         /*
40304         if(Roo.isIE){
40305             this.win.focus();
40306             var r = this.doc.selection.createRange();
40307             if(r){
40308                 r.collapse(true);
40309                 r.pasteHTML(text);
40310                 this.syncValue();
40311                 this.deferFocus();
40312             
40313             }
40314             return;
40315         }
40316         */
40317         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
40318             this.win.focus();
40319             
40320             
40321             // from jquery ui (MIT licenced)
40322             var range, node;
40323             var win = this.win;
40324             
40325             if (win.getSelection && win.getSelection().getRangeAt) {
40326                 range = win.getSelection().getRangeAt(0);
40327                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
40328                 range.insertNode(node);
40329             } else if (win.document.selection && win.document.selection.createRange) {
40330                 // no firefox support
40331                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40332                 win.document.selection.createRange().pasteHTML(txt);
40333             } else {
40334                 // no firefox support
40335                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40336                 this.execCmd('InsertHTML', txt);
40337             } 
40338             
40339             this.syncValue();
40340             
40341             this.deferFocus();
40342         }
40343     },
40344  // private
40345     mozKeyPress : function(e){
40346         if(e.ctrlKey){
40347             var c = e.getCharCode(), cmd;
40348           
40349             if(c > 0){
40350                 c = String.fromCharCode(c).toLowerCase();
40351                 switch(c){
40352                     case 'b':
40353                         cmd = 'bold';
40354                         break;
40355                     case 'i':
40356                         cmd = 'italic';
40357                         break;
40358                     
40359                     case 'u':
40360                         cmd = 'underline';
40361                         break;
40362                     
40363                     case 'v':
40364                         this.cleanUpPaste.defer(100, this);
40365                         return;
40366                         
40367                 }
40368                 if(cmd){
40369                     this.win.focus();
40370                     this.execCmd(cmd);
40371                     this.deferFocus();
40372                     e.preventDefault();
40373                 }
40374                 
40375             }
40376         }
40377     },
40378
40379     // private
40380     fixKeys : function(){ // load time branching for fastest keydown performance
40381         if(Roo.isIE){
40382             return function(e){
40383                 var k = e.getKey(), r;
40384                 if(k == e.TAB){
40385                     e.stopEvent();
40386                     r = this.doc.selection.createRange();
40387                     if(r){
40388                         r.collapse(true);
40389                         r.pasteHTML('&#160;&#160;&#160;&#160;');
40390                         this.deferFocus();
40391                     }
40392                     return;
40393                 }
40394                 
40395                 if(k == e.ENTER){
40396                     r = this.doc.selection.createRange();
40397                     if(r){
40398                         var target = r.parentElement();
40399                         if(!target || target.tagName.toLowerCase() != 'li'){
40400                             e.stopEvent();
40401                             r.pasteHTML('<br />');
40402                             r.collapse(false);
40403                             r.select();
40404                         }
40405                     }
40406                 }
40407                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40408                     this.cleanUpPaste.defer(100, this);
40409                     return;
40410                 }
40411                 
40412                 
40413             };
40414         }else if(Roo.isOpera){
40415             return function(e){
40416                 var k = e.getKey();
40417                 if(k == e.TAB){
40418                     e.stopEvent();
40419                     this.win.focus();
40420                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
40421                     this.deferFocus();
40422                 }
40423                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40424                     this.cleanUpPaste.defer(100, this);
40425                     return;
40426                 }
40427                 
40428             };
40429         }else if(Roo.isSafari){
40430             return function(e){
40431                 var k = e.getKey();
40432                 
40433                 if(k == e.TAB){
40434                     e.stopEvent();
40435                     this.execCmd('InsertText','\t');
40436                     this.deferFocus();
40437                     return;
40438                 }
40439                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40440                     this.cleanUpPaste.defer(100, this);
40441                     return;
40442                 }
40443                 
40444              };
40445         }
40446     }(),
40447     
40448     getAllAncestors: function()
40449     {
40450         var p = this.getSelectedNode();
40451         var a = [];
40452         if (!p) {
40453             a.push(p); // push blank onto stack..
40454             p = this.getParentElement();
40455         }
40456         
40457         
40458         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
40459             a.push(p);
40460             p = p.parentNode;
40461         }
40462         a.push(this.doc.body);
40463         return a;
40464     },
40465     lastSel : false,
40466     lastSelNode : false,
40467     
40468     
40469     getSelection : function() 
40470     {
40471         this.assignDocWin();
40472         return Roo.isIE ? this.doc.selection : this.win.getSelection();
40473     },
40474     
40475     getSelectedNode: function() 
40476     {
40477         // this may only work on Gecko!!!
40478         
40479         // should we cache this!!!!
40480         
40481         
40482         
40483          
40484         var range = this.createRange(this.getSelection()).cloneRange();
40485         
40486         if (Roo.isIE) {
40487             var parent = range.parentElement();
40488             while (true) {
40489                 var testRange = range.duplicate();
40490                 testRange.moveToElementText(parent);
40491                 if (testRange.inRange(range)) {
40492                     break;
40493                 }
40494                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
40495                     break;
40496                 }
40497                 parent = parent.parentElement;
40498             }
40499             return parent;
40500         }
40501         
40502         // is ancestor a text element.
40503         var ac =  range.commonAncestorContainer;
40504         if (ac.nodeType == 3) {
40505             ac = ac.parentNode;
40506         }
40507         
40508         var ar = ac.childNodes;
40509          
40510         var nodes = [];
40511         var other_nodes = [];
40512         var has_other_nodes = false;
40513         for (var i=0;i<ar.length;i++) {
40514             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
40515                 continue;
40516             }
40517             // fullly contained node.
40518             
40519             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
40520                 nodes.push(ar[i]);
40521                 continue;
40522             }
40523             
40524             // probably selected..
40525             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
40526                 other_nodes.push(ar[i]);
40527                 continue;
40528             }
40529             // outer..
40530             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
40531                 continue;
40532             }
40533             
40534             
40535             has_other_nodes = true;
40536         }
40537         if (!nodes.length && other_nodes.length) {
40538             nodes= other_nodes;
40539         }
40540         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
40541             return false;
40542         }
40543         
40544         return nodes[0];
40545     },
40546     createRange: function(sel)
40547     {
40548         // this has strange effects when using with 
40549         // top toolbar - not sure if it's a great idea.
40550         //this.editor.contentWindow.focus();
40551         if (typeof sel != "undefined") {
40552             try {
40553                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
40554             } catch(e) {
40555                 return this.doc.createRange();
40556             }
40557         } else {
40558             return this.doc.createRange();
40559         }
40560     },
40561     getParentElement: function()
40562     {
40563         
40564         this.assignDocWin();
40565         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
40566         
40567         var range = this.createRange(sel);
40568          
40569         try {
40570             var p = range.commonAncestorContainer;
40571             while (p.nodeType == 3) { // text node
40572                 p = p.parentNode;
40573             }
40574             return p;
40575         } catch (e) {
40576             return null;
40577         }
40578     
40579     },
40580     /***
40581      *
40582      * Range intersection.. the hard stuff...
40583      *  '-1' = before
40584      *  '0' = hits..
40585      *  '1' = after.
40586      *         [ -- selected range --- ]
40587      *   [fail]                        [fail]
40588      *
40589      *    basically..
40590      *      if end is before start or  hits it. fail.
40591      *      if start is after end or hits it fail.
40592      *
40593      *   if either hits (but other is outside. - then it's not 
40594      *   
40595      *    
40596      **/
40597     
40598     
40599     // @see http://www.thismuchiknow.co.uk/?p=64.
40600     rangeIntersectsNode : function(range, node)
40601     {
40602         var nodeRange = node.ownerDocument.createRange();
40603         try {
40604             nodeRange.selectNode(node);
40605         } catch (e) {
40606             nodeRange.selectNodeContents(node);
40607         }
40608     
40609         var rangeStartRange = range.cloneRange();
40610         rangeStartRange.collapse(true);
40611     
40612         var rangeEndRange = range.cloneRange();
40613         rangeEndRange.collapse(false);
40614     
40615         var nodeStartRange = nodeRange.cloneRange();
40616         nodeStartRange.collapse(true);
40617     
40618         var nodeEndRange = nodeRange.cloneRange();
40619         nodeEndRange.collapse(false);
40620     
40621         return rangeStartRange.compareBoundaryPoints(
40622                  Range.START_TO_START, nodeEndRange) == -1 &&
40623                rangeEndRange.compareBoundaryPoints(
40624                  Range.START_TO_START, nodeStartRange) == 1;
40625         
40626          
40627     },
40628     rangeCompareNode : function(range, node)
40629     {
40630         var nodeRange = node.ownerDocument.createRange();
40631         try {
40632             nodeRange.selectNode(node);
40633         } catch (e) {
40634             nodeRange.selectNodeContents(node);
40635         }
40636         
40637         
40638         range.collapse(true);
40639     
40640         nodeRange.collapse(true);
40641      
40642         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
40643         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
40644          
40645         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
40646         
40647         var nodeIsBefore   =  ss == 1;
40648         var nodeIsAfter    = ee == -1;
40649         
40650         if (nodeIsBefore && nodeIsAfter)
40651             return 0; // outer
40652         if (!nodeIsBefore && nodeIsAfter)
40653             return 1; //right trailed.
40654         
40655         if (nodeIsBefore && !nodeIsAfter)
40656             return 2;  // left trailed.
40657         // fully contined.
40658         return 3;
40659     },
40660
40661     // private? - in a new class?
40662     cleanUpPaste :  function()
40663     {
40664         // cleans up the whole document..
40665          Roo.log('cleanuppaste');
40666         this.cleanUpChildren(this.doc.body);
40667         var clean = this.cleanWordChars(this.doc.body.innerHTML);
40668         if (clean != this.doc.body.innerHTML) {
40669             this.doc.body.innerHTML = clean;
40670         }
40671         
40672     },
40673     
40674     cleanWordChars : function(input) {
40675         var he = Roo.form.HtmlEditor;
40676     
40677         var output = input;
40678         Roo.each(he.swapCodes, function(sw) { 
40679         
40680             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
40681             output = output.replace(swapper, sw[1]);
40682         });
40683         return output;
40684     },
40685     
40686     
40687     cleanUpChildren : function (n)
40688     {
40689         if (!n.childNodes.length) {
40690             return;
40691         }
40692         for (var i = n.childNodes.length-1; i > -1 ; i--) {
40693            this.cleanUpChild(n.childNodes[i]);
40694         }
40695     },
40696     
40697     
40698         
40699     
40700     cleanUpChild : function (node)
40701     {
40702         //console.log(node);
40703         if (node.nodeName == "#text") {
40704             // clean up silly Windows -- stuff?
40705             return; 
40706         }
40707         if (node.nodeName == "#comment") {
40708             node.parentNode.removeChild(node);
40709             // clean up silly Windows -- stuff?
40710             return; 
40711         }
40712         
40713         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
40714             // remove node.
40715             node.parentNode.removeChild(node);
40716             return;
40717             
40718         }
40719         
40720         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
40721         
40722         // remove <a name=....> as rendering on yahoo mailer is bored with this.
40723         
40724         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
40725             remove_keep_children = true;
40726         }
40727         
40728         if (remove_keep_children) {
40729             this.cleanUpChildren(node);
40730             // inserts everything just before this node...
40731             while (node.childNodes.length) {
40732                 var cn = node.childNodes[0];
40733                 node.removeChild(cn);
40734                 node.parentNode.insertBefore(cn, node);
40735             }
40736             node.parentNode.removeChild(node);
40737             return;
40738         }
40739         
40740         if (!node.attributes || !node.attributes.length) {
40741             this.cleanUpChildren(node);
40742             return;
40743         }
40744         
40745         function cleanAttr(n,v)
40746         {
40747             
40748             if (v.match(/^\./) || v.match(/^\//)) {
40749                 return;
40750             }
40751             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
40752                 return;
40753             }
40754             if (v.match(/^#/)) {
40755                 return;
40756             }
40757             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
40758             node.removeAttribute(n);
40759             
40760         }
40761         
40762         function cleanStyle(n,v)
40763         {
40764             if (v.match(/expression/)) { //XSS?? should we even bother..
40765                 node.removeAttribute(n);
40766                 return;
40767             }
40768             
40769             
40770             var parts = v.split(/;/);
40771             Roo.each(parts, function(p) {
40772                 p = p.replace(/\s+/g,'');
40773                 if (!p.length) {
40774                     return true;
40775                 }
40776                 var l = p.split(':').shift().replace(/\s+/g,'');
40777                 
40778                 // only allow 'c whitelisted system attributes'
40779                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
40780                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
40781                     node.removeAttribute(n);
40782                     return false;
40783                 }
40784                 return true;
40785             });
40786             
40787             
40788         }
40789         
40790         
40791         for (var i = node.attributes.length-1; i > -1 ; i--) {
40792             var a = node.attributes[i];
40793             //console.log(a);
40794             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
40795                 node.removeAttribute(a.name);
40796                 continue;
40797             }
40798             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
40799                 cleanAttr(a.name,a.value); // fixme..
40800                 continue;
40801             }
40802             if (a.name == 'style') {
40803                 cleanStyle(a.name,a.value);
40804                 continue;
40805             }
40806             /// clean up MS crap..
40807             // tecnically this should be a list of valid class'es..
40808             
40809             
40810             if (a.name == 'class') {
40811                 if (a.value.match(/^Mso/)) {
40812                     node.className = '';
40813                 }
40814                 
40815                 if (a.value.match(/body/)) {
40816                     node.className = '';
40817                 }
40818                 continue;
40819             }
40820             
40821             // style cleanup!?
40822             // class cleanup?
40823             
40824         }
40825         
40826         
40827         this.cleanUpChildren(node);
40828         
40829         
40830     }
40831     
40832     
40833     // hide stuff that is not compatible
40834     /**
40835      * @event blur
40836      * @hide
40837      */
40838     /**
40839      * @event change
40840      * @hide
40841      */
40842     /**
40843      * @event focus
40844      * @hide
40845      */
40846     /**
40847      * @event specialkey
40848      * @hide
40849      */
40850     /**
40851      * @cfg {String} fieldClass @hide
40852      */
40853     /**
40854      * @cfg {String} focusClass @hide
40855      */
40856     /**
40857      * @cfg {String} autoCreate @hide
40858      */
40859     /**
40860      * @cfg {String} inputType @hide
40861      */
40862     /**
40863      * @cfg {String} invalidClass @hide
40864      */
40865     /**
40866      * @cfg {String} invalidText @hide
40867      */
40868     /**
40869      * @cfg {String} msgFx @hide
40870      */
40871     /**
40872      * @cfg {String} validateOnBlur @hide
40873      */
40874 });
40875
40876 Roo.form.HtmlEditor.white = [
40877         'area', 'br', 'img', 'input', 'hr', 'wbr',
40878         
40879        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
40880        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
40881        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
40882        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
40883        'table',   'ul',         'xmp', 
40884        
40885        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
40886       'thead',   'tr', 
40887      
40888       'dir', 'menu', 'ol', 'ul', 'dl',
40889        
40890       'embed',  'object'
40891 ];
40892
40893
40894 Roo.form.HtmlEditor.black = [
40895     //    'embed',  'object', // enable - backend responsiblity to clean thiese
40896         'applet', // 
40897         'base',   'basefont', 'bgsound', 'blink',  'body', 
40898         'frame',  'frameset', 'head',    'html',   'ilayer', 
40899         'iframe', 'layer',  'link',     'meta',    'object',   
40900         'script', 'style' ,'title',  'xml' // clean later..
40901 ];
40902 Roo.form.HtmlEditor.clean = [
40903     'script', 'style', 'title', 'xml'
40904 ];
40905 Roo.form.HtmlEditor.remove = [
40906     'font'
40907 ];
40908 // attributes..
40909
40910 Roo.form.HtmlEditor.ablack = [
40911     'on'
40912 ];
40913     
40914 Roo.form.HtmlEditor.aclean = [ 
40915     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
40916 ];
40917
40918 // protocols..
40919 Roo.form.HtmlEditor.pwhite= [
40920         'http',  'https',  'mailto'
40921 ];
40922
40923 // white listed style attributes.
40924 Roo.form.HtmlEditor.cwhite= [
40925         'text-align',
40926         'font-size'
40927 ];
40928
40929
40930 Roo.form.HtmlEditor.swapCodes   =[ 
40931     [    8211, "--" ], 
40932     [    8212, "--" ], 
40933     [    8216,  "'" ],  
40934     [    8217, "'" ],  
40935     [    8220, '"' ],  
40936     [    8221, '"' ],  
40937     [    8226, "*" ],  
40938     [    8230, "..." ]
40939 ]; 
40940
40941     // <script type="text/javascript">
40942 /*
40943  * Based on
40944  * Ext JS Library 1.1.1
40945  * Copyright(c) 2006-2007, Ext JS, LLC.
40946  *  
40947  
40948  */
40949
40950 /**
40951  * @class Roo.form.HtmlEditorToolbar1
40952  * Basic Toolbar
40953  * 
40954  * Usage:
40955  *
40956  new Roo.form.HtmlEditor({
40957     ....
40958     toolbars : [
40959         new Roo.form.HtmlEditorToolbar1({
40960             disable : { fonts: 1 , format: 1, ..., ... , ...],
40961             btns : [ .... ]
40962         })
40963     }
40964      
40965  * 
40966  * @cfg {Object} disable List of elements to disable..
40967  * @cfg {Array} btns List of additional buttons.
40968  * 
40969  * 
40970  * NEEDS Extra CSS? 
40971  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
40972  */
40973  
40974 Roo.form.HtmlEditor.ToolbarStandard = function(config)
40975 {
40976     
40977     Roo.apply(this, config);
40978     
40979     // default disabled, based on 'good practice'..
40980     this.disable = this.disable || {};
40981     Roo.applyIf(this.disable, {
40982         fontSize : true,
40983         colors : true,
40984         specialElements : true
40985     });
40986     
40987     
40988     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40989     // dont call parent... till later.
40990 }
40991
40992 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
40993     
40994     tb: false,
40995     
40996     rendered: false,
40997     
40998     editor : false,
40999     /**
41000      * @cfg {Object} disable  List of toolbar elements to disable
41001          
41002      */
41003     disable : false,
41004       /**
41005      * @cfg {Array} fontFamilies An array of available font families
41006      */
41007     fontFamilies : [
41008         'Arial',
41009         'Courier New',
41010         'Tahoma',
41011         'Times New Roman',
41012         'Verdana'
41013     ],
41014     
41015     specialChars : [
41016            "&#169;",
41017           "&#174;",     
41018           "&#8482;",    
41019           "&#163;" ,    
41020          // "&#8212;",    
41021           "&#8230;",    
41022           "&#247;" ,    
41023         //  "&#225;" ,     ?? a acute?
41024            "&#8364;"    , //Euro
41025        //   "&#8220;"    ,
41026         //  "&#8221;"    ,
41027         //  "&#8226;"    ,
41028           "&#176;"  //   , // degrees
41029
41030          // "&#233;"     , // e ecute
41031          // "&#250;"     , // u ecute?
41032     ],
41033     
41034     specialElements : [
41035         {
41036             text: "Insert Table",
41037             xtype: 'MenuItem',
41038             xns : Roo.Menu,
41039             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
41040                 
41041         },
41042         {    
41043             text: "Insert Image",
41044             xtype: 'MenuItem',
41045             xns : Roo.Menu,
41046             ihtml : '<img src="about:blank"/>'
41047             
41048         }
41049         
41050          
41051     ],
41052     
41053     
41054     inputElements : [ 
41055             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
41056             "input:submit", "input:button", "select", "textarea", "label" ],
41057     formats : [
41058         ["p"] ,  
41059         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
41060         ["pre"],[ "code"], 
41061         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
41062     ],
41063      /**
41064      * @cfg {String} defaultFont default font to use.
41065      */
41066     defaultFont: 'tahoma',
41067    
41068     fontSelect : false,
41069     
41070     
41071     formatCombo : false,
41072     
41073     init : function(editor)
41074     {
41075         this.editor = editor;
41076         
41077         
41078         var fid = editor.frameId;
41079         var etb = this;
41080         function btn(id, toggle, handler){
41081             var xid = fid + '-'+ id ;
41082             return {
41083                 id : xid,
41084                 cmd : id,
41085                 cls : 'x-btn-icon x-edit-'+id,
41086                 enableToggle:toggle !== false,
41087                 scope: editor, // was editor...
41088                 handler:handler||editor.relayBtnCmd,
41089                 clickEvent:'mousedown',
41090                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
41091                 tabIndex:-1
41092             };
41093         }
41094         
41095         
41096         
41097         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
41098         this.tb = tb;
41099          // stop form submits
41100         tb.el.on('click', function(e){
41101             e.preventDefault(); // what does this do?
41102         });
41103
41104         if(!this.disable.font && !Roo.isSafari){
41105             /* why no safari for fonts
41106             editor.fontSelect = tb.el.createChild({
41107                 tag:'select',
41108                 tabIndex: -1,
41109                 cls:'x-font-select',
41110                 html: editor.createFontOptions()
41111             });
41112             editor.fontSelect.on('change', function(){
41113                 var font = editor.fontSelect.dom.value;
41114                 editor.relayCmd('fontname', font);
41115                 editor.deferFocus();
41116             }, editor);
41117             tb.add(
41118                 editor.fontSelect.dom,
41119                 '-'
41120             );
41121             */
41122         };
41123         if(!this.disable.formats){
41124             this.formatCombo = new Roo.form.ComboBox({
41125                 store: new Roo.data.SimpleStore({
41126                     id : 'tag',
41127                     fields: ['tag'],
41128                     data : this.formats // from states.js
41129                 }),
41130                 blockFocus : true,
41131                 //autoCreate : {tag: "div",  size: "20"},
41132                 displayField:'tag',
41133                 typeAhead: false,
41134                 mode: 'local',
41135                 editable : false,
41136                 triggerAction: 'all',
41137                 emptyText:'Add tag',
41138                 selectOnFocus:true,
41139                 width:135,
41140                 listeners : {
41141                     'select': function(c, r, i) {
41142                         editor.insertTag(r.get('tag'));
41143                         editor.focus();
41144                     }
41145                 }
41146
41147             });
41148             tb.addField(this.formatCombo);
41149             
41150         }
41151         
41152         if(!this.disable.format){
41153             tb.add(
41154                 btn('bold'),
41155                 btn('italic'),
41156                 btn('underline')
41157             );
41158         };
41159         if(!this.disable.fontSize){
41160             tb.add(
41161                 '-',
41162                 
41163                 
41164                 btn('increasefontsize', false, editor.adjustFont),
41165                 btn('decreasefontsize', false, editor.adjustFont)
41166             );
41167         };
41168         
41169         
41170         if(!this.disable.colors){
41171             tb.add(
41172                 '-', {
41173                     id:editor.frameId +'-forecolor',
41174                     cls:'x-btn-icon x-edit-forecolor',
41175                     clickEvent:'mousedown',
41176                     tooltip: this.buttonTips['forecolor'] || undefined,
41177                     tabIndex:-1,
41178                     menu : new Roo.menu.ColorMenu({
41179                         allowReselect: true,
41180                         focus: Roo.emptyFn,
41181                         value:'000000',
41182                         plain:true,
41183                         selectHandler: function(cp, color){
41184                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
41185                             editor.deferFocus();
41186                         },
41187                         scope: editor,
41188                         clickEvent:'mousedown'
41189                     })
41190                 }, {
41191                     id:editor.frameId +'backcolor',
41192                     cls:'x-btn-icon x-edit-backcolor',
41193                     clickEvent:'mousedown',
41194                     tooltip: this.buttonTips['backcolor'] || undefined,
41195                     tabIndex:-1,
41196                     menu : new Roo.menu.ColorMenu({
41197                         focus: Roo.emptyFn,
41198                         value:'FFFFFF',
41199                         plain:true,
41200                         allowReselect: true,
41201                         selectHandler: function(cp, color){
41202                             if(Roo.isGecko){
41203                                 editor.execCmd('useCSS', false);
41204                                 editor.execCmd('hilitecolor', color);
41205                                 editor.execCmd('useCSS', true);
41206                                 editor.deferFocus();
41207                             }else{
41208                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
41209                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
41210                                 editor.deferFocus();
41211                             }
41212                         },
41213                         scope:editor,
41214                         clickEvent:'mousedown'
41215                     })
41216                 }
41217             );
41218         };
41219         // now add all the items...
41220         
41221
41222         if(!this.disable.alignments){
41223             tb.add(
41224                 '-',
41225                 btn('justifyleft'),
41226                 btn('justifycenter'),
41227                 btn('justifyright')
41228             );
41229         };
41230
41231         //if(!Roo.isSafari){
41232             if(!this.disable.links){
41233                 tb.add(
41234                     '-',
41235                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
41236                 );
41237             };
41238
41239             if(!this.disable.lists){
41240                 tb.add(
41241                     '-',
41242                     btn('insertorderedlist'),
41243                     btn('insertunorderedlist')
41244                 );
41245             }
41246             if(!this.disable.sourceEdit){
41247                 tb.add(
41248                     '-',
41249                     btn('sourceedit', true, function(btn){
41250                         this.toggleSourceEdit(btn.pressed);
41251                     })
41252                 );
41253             }
41254         //}
41255         
41256         var smenu = { };
41257         // special menu.. - needs to be tidied up..
41258         if (!this.disable.special) {
41259             smenu = {
41260                 text: "&#169;",
41261                 cls: 'x-edit-none',
41262                 
41263                 menu : {
41264                     items : []
41265                 }
41266             };
41267             for (var i =0; i < this.specialChars.length; i++) {
41268                 smenu.menu.items.push({
41269                     
41270                     html: this.specialChars[i],
41271                     handler: function(a,b) {
41272                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
41273                         //editor.insertAtCursor(a.html);
41274                         
41275                     },
41276                     tabIndex:-1
41277                 });
41278             }
41279             
41280             
41281             tb.add(smenu);
41282             
41283             
41284         }
41285          
41286         if (!this.disable.specialElements) {
41287             var semenu = {
41288                 text: "Other;",
41289                 cls: 'x-edit-none',
41290                 menu : {
41291                     items : []
41292                 }
41293             };
41294             for (var i =0; i < this.specialElements.length; i++) {
41295                 semenu.menu.items.push(
41296                     Roo.apply({ 
41297                         handler: function(a,b) {
41298                             editor.insertAtCursor(this.ihtml);
41299                         }
41300                     }, this.specialElements[i])
41301                 );
41302                     
41303             }
41304             
41305             tb.add(semenu);
41306             
41307             
41308         }
41309          
41310         
41311         if (this.btns) {
41312             for(var i =0; i< this.btns.length;i++) {
41313                 var b = Roo.factory(this.btns[i],Roo.form);
41314                 b.cls =  'x-edit-none';
41315                 b.scope = editor;
41316                 tb.add(b);
41317             }
41318         
41319         }
41320         
41321         
41322         
41323         // disable everything...
41324         
41325         this.tb.items.each(function(item){
41326            if(item.id != editor.frameId+ '-sourceedit'){
41327                 item.disable();
41328             }
41329         });
41330         this.rendered = true;
41331         
41332         // the all the btns;
41333         editor.on('editorevent', this.updateToolbar, this);
41334         // other toolbars need to implement this..
41335         //editor.on('editmodechange', this.updateToolbar, this);
41336     },
41337     
41338     
41339     
41340     /**
41341      * Protected method that will not generally be called directly. It triggers
41342      * a toolbar update by reading the markup state of the current selection in the editor.
41343      */
41344     updateToolbar: function(){
41345
41346         if(!this.editor.activated){
41347             this.editor.onFirstFocus();
41348             return;
41349         }
41350
41351         var btns = this.tb.items.map, 
41352             doc = this.editor.doc,
41353             frameId = this.editor.frameId;
41354
41355         if(!this.disable.font && !Roo.isSafari){
41356             /*
41357             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
41358             if(name != this.fontSelect.dom.value){
41359                 this.fontSelect.dom.value = name;
41360             }
41361             */
41362         }
41363         if(!this.disable.format){
41364             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
41365             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
41366             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
41367         }
41368         if(!this.disable.alignments){
41369             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
41370             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
41371             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
41372         }
41373         if(!Roo.isSafari && !this.disable.lists){
41374             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
41375             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
41376         }
41377         
41378         var ans = this.editor.getAllAncestors();
41379         if (this.formatCombo) {
41380             
41381             
41382             var store = this.formatCombo.store;
41383             this.formatCombo.setValue("");
41384             for (var i =0; i < ans.length;i++) {
41385                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
41386                     // select it..
41387                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
41388                     break;
41389                 }
41390             }
41391         }
41392         
41393         
41394         
41395         // hides menus... - so this cant be on a menu...
41396         Roo.menu.MenuMgr.hideAll();
41397
41398         //this.editorsyncValue();
41399     },
41400    
41401     
41402     createFontOptions : function(){
41403         var buf = [], fs = this.fontFamilies, ff, lc;
41404         for(var i = 0, len = fs.length; i< len; i++){
41405             ff = fs[i];
41406             lc = ff.toLowerCase();
41407             buf.push(
41408                 '<option value="',lc,'" style="font-family:',ff,';"',
41409                     (this.defaultFont == lc ? ' selected="true">' : '>'),
41410                     ff,
41411                 '</option>'
41412             );
41413         }
41414         return buf.join('');
41415     },
41416     
41417     toggleSourceEdit : function(sourceEditMode){
41418         if(sourceEditMode === undefined){
41419             sourceEditMode = !this.sourceEditMode;
41420         }
41421         this.sourceEditMode = sourceEditMode === true;
41422         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
41423         // just toggle the button?
41424         if(btn.pressed !== this.editor.sourceEditMode){
41425             btn.toggle(this.editor.sourceEditMode);
41426             return;
41427         }
41428         
41429         if(this.sourceEditMode){
41430             this.tb.items.each(function(item){
41431                 if(item.cmd != 'sourceedit'){
41432                     item.disable();
41433                 }
41434             });
41435           
41436         }else{
41437             if(this.initialized){
41438                 this.tb.items.each(function(item){
41439                     item.enable();
41440                 });
41441             }
41442             
41443         }
41444         // tell the editor that it's been pressed..
41445         this.editor.toggleSourceEdit(sourceEditMode);
41446        
41447     },
41448      /**
41449      * Object collection of toolbar tooltips for the buttons in the editor. The key
41450      * is the command id associated with that button and the value is a valid QuickTips object.
41451      * For example:
41452 <pre><code>
41453 {
41454     bold : {
41455         title: 'Bold (Ctrl+B)',
41456         text: 'Make the selected text bold.',
41457         cls: 'x-html-editor-tip'
41458     },
41459     italic : {
41460         title: 'Italic (Ctrl+I)',
41461         text: 'Make the selected text italic.',
41462         cls: 'x-html-editor-tip'
41463     },
41464     ...
41465 </code></pre>
41466     * @type Object
41467      */
41468     buttonTips : {
41469         bold : {
41470             title: 'Bold (Ctrl+B)',
41471             text: 'Make the selected text bold.',
41472             cls: 'x-html-editor-tip'
41473         },
41474         italic : {
41475             title: 'Italic (Ctrl+I)',
41476             text: 'Make the selected text italic.',
41477             cls: 'x-html-editor-tip'
41478         },
41479         underline : {
41480             title: 'Underline (Ctrl+U)',
41481             text: 'Underline the selected text.',
41482             cls: 'x-html-editor-tip'
41483         },
41484         increasefontsize : {
41485             title: 'Grow Text',
41486             text: 'Increase the font size.',
41487             cls: 'x-html-editor-tip'
41488         },
41489         decreasefontsize : {
41490             title: 'Shrink Text',
41491             text: 'Decrease the font size.',
41492             cls: 'x-html-editor-tip'
41493         },
41494         backcolor : {
41495             title: 'Text Highlight Color',
41496             text: 'Change the background color of the selected text.',
41497             cls: 'x-html-editor-tip'
41498         },
41499         forecolor : {
41500             title: 'Font Color',
41501             text: 'Change the color of the selected text.',
41502             cls: 'x-html-editor-tip'
41503         },
41504         justifyleft : {
41505             title: 'Align Text Left',
41506             text: 'Align text to the left.',
41507             cls: 'x-html-editor-tip'
41508         },
41509         justifycenter : {
41510             title: 'Center Text',
41511             text: 'Center text in the editor.',
41512             cls: 'x-html-editor-tip'
41513         },
41514         justifyright : {
41515             title: 'Align Text Right',
41516             text: 'Align text to the right.',
41517             cls: 'x-html-editor-tip'
41518         },
41519         insertunorderedlist : {
41520             title: 'Bullet List',
41521             text: 'Start a bulleted list.',
41522             cls: 'x-html-editor-tip'
41523         },
41524         insertorderedlist : {
41525             title: 'Numbered List',
41526             text: 'Start a numbered list.',
41527             cls: 'x-html-editor-tip'
41528         },
41529         createlink : {
41530             title: 'Hyperlink',
41531             text: 'Make the selected text a hyperlink.',
41532             cls: 'x-html-editor-tip'
41533         },
41534         sourceedit : {
41535             title: 'Source Edit',
41536             text: 'Switch to source editing mode.',
41537             cls: 'x-html-editor-tip'
41538         }
41539     },
41540     // private
41541     onDestroy : function(){
41542         if(this.rendered){
41543             
41544             this.tb.items.each(function(item){
41545                 if(item.menu){
41546                     item.menu.removeAll();
41547                     if(item.menu.el){
41548                         item.menu.el.destroy();
41549                     }
41550                 }
41551                 item.destroy();
41552             });
41553              
41554         }
41555     },
41556     onFirstFocus: function() {
41557         this.tb.items.each(function(item){
41558            item.enable();
41559         });
41560     }
41561 });
41562
41563
41564
41565
41566 // <script type="text/javascript">
41567 /*
41568  * Based on
41569  * Ext JS Library 1.1.1
41570  * Copyright(c) 2006-2007, Ext JS, LLC.
41571  *  
41572  
41573  */
41574
41575  
41576 /**
41577  * @class Roo.form.HtmlEditor.ToolbarContext
41578  * Context Toolbar
41579  * 
41580  * Usage:
41581  *
41582  new Roo.form.HtmlEditor({
41583     ....
41584     toolbars : [
41585         { xtype: 'ToolbarStandard', styles : {} }
41586         { xtype: 'ToolbarContext', disable : {} }
41587     ]
41588 })
41589
41590      
41591  * 
41592  * @config : {Object} disable List of elements to disable.. (not done yet.)
41593  * @config : {Object} styles  Map of styles available.
41594  * 
41595  */
41596
41597 Roo.form.HtmlEditor.ToolbarContext = function(config)
41598 {
41599     
41600     Roo.apply(this, config);
41601     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
41602     // dont call parent... till later.
41603     this.styles = this.styles || {};
41604 }
41605 Roo.form.HtmlEditor.ToolbarContext.types = {
41606     'IMG' : {
41607         width : {
41608             title: "Width",
41609             width: 40
41610         },
41611         height:  {
41612             title: "Height",
41613             width: 40
41614         },
41615         align: {
41616             title: "Align",
41617             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
41618             width : 80
41619             
41620         },
41621         border: {
41622             title: "Border",
41623             width: 40
41624         },
41625         alt: {
41626             title: "Alt",
41627             width: 120
41628         },
41629         src : {
41630             title: "Src",
41631             width: 220
41632         }
41633         
41634     },
41635     'A' : {
41636         name : {
41637             title: "Name",
41638             width: 50
41639         },
41640         href:  {
41641             title: "Href",
41642             width: 220
41643         } // border?
41644         
41645     },
41646     'TABLE' : {
41647         rows : {
41648             title: "Rows",
41649             width: 20
41650         },
41651         cols : {
41652             title: "Cols",
41653             width: 20
41654         },
41655         width : {
41656             title: "Width",
41657             width: 40
41658         },
41659         height : {
41660             title: "Height",
41661             width: 40
41662         },
41663         border : {
41664             title: "Border",
41665             width: 20
41666         }
41667     },
41668     'TD' : {
41669         width : {
41670             title: "Width",
41671             width: 40
41672         },
41673         height : {
41674             title: "Height",
41675             width: 40
41676         },   
41677         align: {
41678             title: "Align",
41679             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
41680             width: 80
41681         },
41682         valign: {
41683             title: "Valign",
41684             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
41685             width: 80
41686         },
41687         colspan: {
41688             title: "Colspan",
41689             width: 20
41690             
41691         }
41692     },
41693     'INPUT' : {
41694         name : {
41695             title: "name",
41696             width: 120
41697         },
41698         value : {
41699             title: "Value",
41700             width: 120
41701         },
41702         width : {
41703             title: "Width",
41704             width: 40
41705         }
41706     },
41707     'LABEL' : {
41708         'for' : {
41709             title: "For",
41710             width: 120
41711         }
41712     },
41713     'TEXTAREA' : {
41714           name : {
41715             title: "name",
41716             width: 120
41717         },
41718         rows : {
41719             title: "Rows",
41720             width: 20
41721         },
41722         cols : {
41723             title: "Cols",
41724             width: 20
41725         }
41726     },
41727     'SELECT' : {
41728         name : {
41729             title: "name",
41730             width: 120
41731         },
41732         selectoptions : {
41733             title: "Options",
41734             width: 200
41735         }
41736     },
41737     
41738     // should we really allow this??
41739     // should this just be 
41740     'BODY' : {
41741         title : {
41742             title: "title",
41743             width: 200,
41744             disabled : true
41745         }
41746     },
41747     '*' : {
41748         // empty..
41749     }
41750 };
41751
41752
41753
41754 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
41755     
41756     tb: false,
41757     
41758     rendered: false,
41759     
41760     editor : false,
41761     /**
41762      * @cfg {Object} disable  List of toolbar elements to disable
41763          
41764      */
41765     disable : false,
41766     /**
41767      * @cfg {Object} styles List of styles 
41768      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
41769      *
41770      * These must be defined in the page, so they get rendered correctly..
41771      * .headline { }
41772      * TD.underline { }
41773      * 
41774      */
41775     styles : false,
41776     
41777     
41778     
41779     toolbars : false,
41780     
41781     init : function(editor)
41782     {
41783         this.editor = editor;
41784         
41785         
41786         var fid = editor.frameId;
41787         var etb = this;
41788         function btn(id, toggle, handler){
41789             var xid = fid + '-'+ id ;
41790             return {
41791                 id : xid,
41792                 cmd : id,
41793                 cls : 'x-btn-icon x-edit-'+id,
41794                 enableToggle:toggle !== false,
41795                 scope: editor, // was editor...
41796                 handler:handler||editor.relayBtnCmd,
41797                 clickEvent:'mousedown',
41798                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
41799                 tabIndex:-1
41800             };
41801         }
41802         // create a new element.
41803         var wdiv = editor.wrap.createChild({
41804                 tag: 'div'
41805             }, editor.wrap.dom.firstChild.nextSibling, true);
41806         
41807         // can we do this more than once??
41808         
41809          // stop form submits
41810       
41811  
41812         // disable everything...
41813         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
41814         this.toolbars = {};
41815            
41816         for (var i in  ty) {
41817           
41818             this.toolbars[i] = this.buildToolbar(ty[i],i);
41819         }
41820         this.tb = this.toolbars.BODY;
41821         this.tb.el.show();
41822         this.buildFooter();
41823         this.footer.show();
41824         editor.on('hide', function( ) { this.footer.hide() }, this);
41825         editor.on('show', function( ) { this.footer.show() }, this);
41826         
41827          
41828         this.rendered = true;
41829         
41830         // the all the btns;
41831         editor.on('editorevent', this.updateToolbar, this);
41832         // other toolbars need to implement this..
41833         //editor.on('editmodechange', this.updateToolbar, this);
41834     },
41835     
41836     
41837     
41838     /**
41839      * Protected method that will not generally be called directly. It triggers
41840      * a toolbar update by reading the markup state of the current selection in the editor.
41841      */
41842     updateToolbar: function(editor,ev,sel){
41843
41844         //Roo.log(ev);
41845         // capture mouse up - this is handy for selecting images..
41846         // perhaps should go somewhere else...
41847         if(!this.editor.activated){
41848              this.editor.onFirstFocus();
41849             return;
41850         }
41851         
41852         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
41853         // selectNode - might want to handle IE?
41854         if (ev &&
41855             (ev.type == 'mouseup' || ev.type == 'click' ) &&
41856             ev.target && ev.target.tagName == 'IMG') {
41857             // they have click on an image...
41858             // let's see if we can change the selection...
41859             sel = ev.target;
41860          
41861               var nodeRange = sel.ownerDocument.createRange();
41862             try {
41863                 nodeRange.selectNode(sel);
41864             } catch (e) {
41865                 nodeRange.selectNodeContents(sel);
41866             }
41867             //nodeRange.collapse(true);
41868             var s = editor.win.getSelection();
41869             s.removeAllRanges();
41870             s.addRange(nodeRange);
41871         }  
41872         
41873       
41874         var updateFooter = sel ? false : true;
41875         
41876         
41877         var ans = this.editor.getAllAncestors();
41878         
41879         // pick
41880         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
41881         
41882         if (!sel) { 
41883             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
41884             sel = sel ? sel : this.editor.doc.body;
41885             sel = sel.tagName.length ? sel : this.editor.doc.body;
41886             
41887         }
41888         // pick a menu that exists..
41889         var tn = sel.tagName.toUpperCase();
41890         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
41891         
41892         tn = sel.tagName.toUpperCase();
41893         
41894         var lastSel = this.tb.selectedNode
41895         
41896         this.tb.selectedNode = sel;
41897         
41898         // if current menu does not match..
41899         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
41900                 
41901             this.tb.el.hide();
41902             ///console.log("show: " + tn);
41903             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
41904             this.tb.el.show();
41905             // update name
41906             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
41907             
41908             
41909             // update attributes
41910             if (this.tb.fields) {
41911                 this.tb.fields.each(function(e) {
41912                    e.setValue(sel.getAttribute(e.attrname));
41913                 });
41914             }
41915             
41916             var hasStyles = false;
41917             for(var i in this.styles) {
41918                 hasStyles = true;
41919                 break;
41920             }
41921             
41922             // update styles
41923             if (hasStyles) { 
41924                 var st = this.tb.fields.item(0);
41925                 
41926                 st.store.removeAll();
41927                
41928                 
41929                 var cn = sel.className.split(/\s+/);
41930                 
41931                 var avs = [];
41932                 if (this.styles['*']) {
41933                     
41934                     Roo.each(this.styles['*'], function(v) {
41935                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
41936                     });
41937                 }
41938                 if (this.styles[tn]) { 
41939                     Roo.each(this.styles[tn], function(v) {
41940                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
41941                     });
41942                 }
41943                 
41944                 st.store.loadData(avs);
41945                 st.collapse();
41946                 st.setValue(cn);
41947             }
41948             // flag our selected Node.
41949             this.tb.selectedNode = sel;
41950            
41951            
41952             Roo.menu.MenuMgr.hideAll();
41953
41954         }
41955         
41956         if (!updateFooter) {
41957             return;
41958         }
41959         // update the footer
41960         //
41961         var html = '';
41962         
41963         this.footerEls = ans.reverse();
41964         Roo.each(this.footerEls, function(a,i) {
41965             if (!a) { return; }
41966             html += html.length ? ' &gt; '  :  '';
41967             
41968             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
41969             
41970         });
41971        
41972         // 
41973         var sz = this.footDisp.up('td').getSize();
41974         this.footDisp.dom.style.width = (sz.width -10) + 'px';
41975         this.footDisp.dom.style.marginLeft = '5px';
41976         
41977         this.footDisp.dom.style.overflow = 'hidden';
41978         
41979         this.footDisp.dom.innerHTML = html;
41980             
41981         //this.editorsyncValue();
41982     },
41983    
41984        
41985     // private
41986     onDestroy : function(){
41987         if(this.rendered){
41988             
41989             this.tb.items.each(function(item){
41990                 if(item.menu){
41991                     item.menu.removeAll();
41992                     if(item.menu.el){
41993                         item.menu.el.destroy();
41994                     }
41995                 }
41996                 item.destroy();
41997             });
41998              
41999         }
42000     },
42001     onFirstFocus: function() {
42002         // need to do this for all the toolbars..
42003         this.tb.items.each(function(item){
42004            item.enable();
42005         });
42006     },
42007     buildToolbar: function(tlist, nm)
42008     {
42009         var editor = this.editor;
42010          // create a new element.
42011         var wdiv = editor.wrap.createChild({
42012                 tag: 'div'
42013             }, editor.wrap.dom.firstChild.nextSibling, true);
42014         
42015        
42016         var tb = new Roo.Toolbar(wdiv);
42017         // add the name..
42018         
42019         tb.add(nm+ ":&nbsp;");
42020         
42021         var styles = [];
42022         for(var i in this.styles) {
42023             styles.push(i);
42024         }
42025         
42026         // styles...
42027         if (styles && styles.length) {
42028             
42029             // this needs a multi-select checkbox...
42030             tb.addField( new Roo.form.ComboBox({
42031                 store: new Roo.data.SimpleStore({
42032                     id : 'val',
42033                     fields: ['val', 'selected'],
42034                     data : [] 
42035                 }),
42036                 name : '-roo-edit-className',
42037                 attrname : 'className',
42038                 displayField:'val',
42039                 typeAhead: false,
42040                 mode: 'local',
42041                 editable : false,
42042                 triggerAction: 'all',
42043                 emptyText:'Select Style',
42044                 selectOnFocus:true,
42045                 width: 130,
42046                 listeners : {
42047                     'select': function(c, r, i) {
42048                         // initial support only for on class per el..
42049                         tb.selectedNode.className =  r ? r.get('val') : '';
42050                         editor.syncValue();
42051                     }
42052                 }
42053     
42054             }));
42055         }
42056             
42057         
42058         
42059         for (var i in tlist) {
42060             
42061             var item = tlist[i];
42062             tb.add(item.title + ":&nbsp;");
42063             
42064             
42065             
42066             
42067             if (item.opts) {
42068                 // opts == pulldown..
42069                 tb.addField( new Roo.form.ComboBox({
42070                     store: new Roo.data.SimpleStore({
42071                         id : 'val',
42072                         fields: ['val'],
42073                         data : item.opts  
42074                     }),
42075                     name : '-roo-edit-' + i,
42076                     attrname : i,
42077                     displayField:'val',
42078                     typeAhead: false,
42079                     mode: 'local',
42080                     editable : false,
42081                     triggerAction: 'all',
42082                     emptyText:'Select',
42083                     selectOnFocus:true,
42084                     width: item.width ? item.width  : 130,
42085                     listeners : {
42086                         'select': function(c, r, i) {
42087                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
42088                         }
42089                     }
42090
42091                 }));
42092                 continue;
42093                     
42094                  
42095                 
42096                 tb.addField( new Roo.form.TextField({
42097                     name: i,
42098                     width: 100,
42099                     //allowBlank:false,
42100                     value: ''
42101                 }));
42102                 continue;
42103             }
42104             tb.addField( new Roo.form.TextField({
42105                 name: '-roo-edit-' + i,
42106                 attrname : i,
42107                 
42108                 width: item.width,
42109                 //allowBlank:true,
42110                 value: '',
42111                 listeners: {
42112                     'change' : function(f, nv, ov) {
42113                         tb.selectedNode.setAttribute(f.attrname, nv);
42114                     }
42115                 }
42116             }));
42117              
42118         }
42119         tb.el.on('click', function(e){
42120             e.preventDefault(); // what does this do?
42121         });
42122         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
42123         tb.el.hide();
42124         tb.name = nm;
42125         // dont need to disable them... as they will get hidden
42126         return tb;
42127          
42128         
42129     },
42130     buildFooter : function()
42131     {
42132         
42133         var fel = this.editor.wrap.createChild();
42134         this.footer = new Roo.Toolbar(fel);
42135         // toolbar has scrolly on left / right?
42136         var footDisp= new Roo.Toolbar.Fill();
42137         var _t = this;
42138         this.footer.add(
42139             {
42140                 text : '&lt;',
42141                 xtype: 'Button',
42142                 handler : function() {
42143                     _t.footDisp.scrollTo('left',0,true)
42144                 }
42145             }
42146         );
42147         this.footer.add( footDisp );
42148         this.footer.add( 
42149             {
42150                 text : '&gt;',
42151                 xtype: 'Button',
42152                 handler : function() {
42153                     // no animation..
42154                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
42155                 }
42156             }
42157         );
42158         var fel = Roo.get(footDisp.el);
42159         fel.addClass('x-editor-context');
42160         this.footDispWrap = fel; 
42161         this.footDispWrap.overflow  = 'hidden';
42162         
42163         this.footDisp = fel.createChild();
42164         this.footDispWrap.on('click', this.onContextClick, this)
42165         
42166         
42167     },
42168     onContextClick : function (ev,dom)
42169     {
42170         ev.preventDefault();
42171         var  cn = dom.className;
42172         Roo.log(cn);
42173         if (!cn.match(/x-ed-loc-/)) {
42174             return;
42175         }
42176         var n = cn.split('-').pop();
42177         var ans = this.footerEls;
42178         var sel = ans[n];
42179         
42180          // pick
42181         var range = this.editor.createRange();
42182         
42183         range.selectNodeContents(sel);
42184         //range.selectNode(sel);
42185         
42186         
42187         var selection = this.editor.getSelection();
42188         selection.removeAllRanges();
42189         selection.addRange(range);
42190         
42191         
42192         
42193         this.updateToolbar(null, null, sel);
42194         
42195         
42196     }
42197     
42198     
42199     
42200     
42201     
42202 });
42203
42204
42205
42206
42207
42208 /*
42209  * Based on:
42210  * Ext JS Library 1.1.1
42211  * Copyright(c) 2006-2007, Ext JS, LLC.
42212  *
42213  * Originally Released Under LGPL - original licence link has changed is not relivant.
42214  *
42215  * Fork - LGPL
42216  * <script type="text/javascript">
42217  */
42218  
42219 /**
42220  * @class Roo.form.BasicForm
42221  * @extends Roo.util.Observable
42222  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
42223  * @constructor
42224  * @param {String/HTMLElement/Roo.Element} el The form element or its id
42225  * @param {Object} config Configuration options
42226  */
42227 Roo.form.BasicForm = function(el, config){
42228     this.allItems = [];
42229     this.childForms = [];
42230     Roo.apply(this, config);
42231     /*
42232      * The Roo.form.Field items in this form.
42233      * @type MixedCollection
42234      */
42235      
42236      
42237     this.items = new Roo.util.MixedCollection(false, function(o){
42238         return o.id || (o.id = Roo.id());
42239     });
42240     this.addEvents({
42241         /**
42242          * @event beforeaction
42243          * Fires before any action is performed. Return false to cancel the action.
42244          * @param {Form} this
42245          * @param {Action} action The action to be performed
42246          */
42247         beforeaction: true,
42248         /**
42249          * @event actionfailed
42250          * Fires when an action fails.
42251          * @param {Form} this
42252          * @param {Action} action The action that failed
42253          */
42254         actionfailed : true,
42255         /**
42256          * @event actioncomplete
42257          * Fires when an action is completed.
42258          * @param {Form} this
42259          * @param {Action} action The action that completed
42260          */
42261         actioncomplete : true
42262     });
42263     if(el){
42264         this.initEl(el);
42265     }
42266     Roo.form.BasicForm.superclass.constructor.call(this);
42267 };
42268
42269 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
42270     /**
42271      * @cfg {String} method
42272      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
42273      */
42274     /**
42275      * @cfg {DataReader} reader
42276      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
42277      * This is optional as there is built-in support for processing JSON.
42278      */
42279     /**
42280      * @cfg {DataReader} errorReader
42281      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
42282      * This is completely optional as there is built-in support for processing JSON.
42283      */
42284     /**
42285      * @cfg {String} url
42286      * The URL to use for form actions if one isn't supplied in the action options.
42287      */
42288     /**
42289      * @cfg {Boolean} fileUpload
42290      * Set to true if this form is a file upload.
42291      */
42292      
42293     /**
42294      * @cfg {Object} baseParams
42295      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
42296      */
42297      /**
42298      
42299     /**
42300      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
42301      */
42302     timeout: 30,
42303
42304     // private
42305     activeAction : null,
42306
42307     /**
42308      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
42309      * or setValues() data instead of when the form was first created.
42310      */
42311     trackResetOnLoad : false,
42312     
42313     
42314     /**
42315      * childForms - used for multi-tab forms
42316      * @type {Array}
42317      */
42318     childForms : false,
42319     
42320     /**
42321      * allItems - full list of fields.
42322      * @type {Array}
42323      */
42324     allItems : false,
42325     
42326     /**
42327      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
42328      * element by passing it or its id or mask the form itself by passing in true.
42329      * @type Mixed
42330      */
42331     waitMsgTarget : false,
42332
42333     // private
42334     initEl : function(el){
42335         this.el = Roo.get(el);
42336         this.id = this.el.id || Roo.id();
42337         this.el.on('submit', this.onSubmit, this);
42338         this.el.addClass('x-form');
42339     },
42340
42341     // private
42342     onSubmit : function(e){
42343         e.stopEvent();
42344     },
42345
42346     /**
42347      * Returns true if client-side validation on the form is successful.
42348      * @return Boolean
42349      */
42350     isValid : function(){
42351         var valid = true;
42352         this.items.each(function(f){
42353            if(!f.validate()){
42354                valid = false;
42355            }
42356         });
42357         return valid;
42358     },
42359
42360     /**
42361      * Returns true if any fields in this form have changed since their original load.
42362      * @return Boolean
42363      */
42364     isDirty : function(){
42365         var dirty = false;
42366         this.items.each(function(f){
42367            if(f.isDirty()){
42368                dirty = true;
42369                return false;
42370            }
42371         });
42372         return dirty;
42373     },
42374
42375     /**
42376      * Performs a predefined action (submit or load) or custom actions you define on this form.
42377      * @param {String} actionName The name of the action type
42378      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
42379      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
42380      * accept other config options):
42381      * <pre>
42382 Property          Type             Description
42383 ----------------  ---------------  ----------------------------------------------------------------------------------
42384 url               String           The url for the action (defaults to the form's url)
42385 method            String           The form method to use (defaults to the form's method, or POST if not defined)
42386 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
42387 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
42388                                    validate the form on the client (defaults to false)
42389      * </pre>
42390      * @return {BasicForm} this
42391      */
42392     doAction : function(action, options){
42393         if(typeof action == 'string'){
42394             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
42395         }
42396         if(this.fireEvent('beforeaction', this, action) !== false){
42397             this.beforeAction(action);
42398             action.run.defer(100, action);
42399         }
42400         return this;
42401     },
42402
42403     /**
42404      * Shortcut to do a submit action.
42405      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
42406      * @return {BasicForm} this
42407      */
42408     submit : function(options){
42409         this.doAction('submit', options);
42410         return this;
42411     },
42412
42413     /**
42414      * Shortcut to do a load action.
42415      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
42416      * @return {BasicForm} this
42417      */
42418     load : function(options){
42419         this.doAction('load', options);
42420         return this;
42421     },
42422
42423     /**
42424      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
42425      * @param {Record} record The record to edit
42426      * @return {BasicForm} this
42427      */
42428     updateRecord : function(record){
42429         record.beginEdit();
42430         var fs = record.fields;
42431         fs.each(function(f){
42432             var field = this.findField(f.name);
42433             if(field){
42434                 record.set(f.name, field.getValue());
42435             }
42436         }, this);
42437         record.endEdit();
42438         return this;
42439     },
42440
42441     /**
42442      * Loads an Roo.data.Record into this form.
42443      * @param {Record} record The record to load
42444      * @return {BasicForm} this
42445      */
42446     loadRecord : function(record){
42447         this.setValues(record.data);
42448         return this;
42449     },
42450
42451     // private
42452     beforeAction : function(action){
42453         var o = action.options;
42454         
42455        
42456         if(this.waitMsgTarget === true){
42457             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
42458         }else if(this.waitMsgTarget){
42459             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
42460             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
42461         }else {
42462             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
42463         }
42464          
42465     },
42466
42467     // private
42468     afterAction : function(action, success){
42469         this.activeAction = null;
42470         var o = action.options;
42471         
42472         if(this.waitMsgTarget === true){
42473             this.el.unmask();
42474         }else if(this.waitMsgTarget){
42475             this.waitMsgTarget.unmask();
42476         }else{
42477             Roo.MessageBox.updateProgress(1);
42478             Roo.MessageBox.hide();
42479         }
42480          
42481         if(success){
42482             if(o.reset){
42483                 this.reset();
42484             }
42485             Roo.callback(o.success, o.scope, [this, action]);
42486             this.fireEvent('actioncomplete', this, action);
42487             
42488         }else{
42489             
42490             // failure condition..
42491             // we have a scenario where updates need confirming.
42492             // eg. if a locking scenario exists..
42493             // we look for { errors : { needs_confirm : true }} in the response.
42494             if (
42495                 (typeof(action.result) != 'undefined')  &&
42496                 (typeof(action.result.errors) != 'undefined')  &&
42497                 (typeof(action.result.errors.needs_confirm) != 'undefined')
42498           ){
42499                 var _t = this;
42500                 Roo.MessageBox.confirm(
42501                     "Change requires confirmation",
42502                     action.result.errorMsg,
42503                     function(r) {
42504                         if (r != 'yes') {
42505                             return;
42506                         }
42507                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
42508                     }
42509                     
42510                 );
42511                 
42512                 
42513                 
42514                 return;
42515             }
42516             
42517             Roo.callback(o.failure, o.scope, [this, action]);
42518             // show an error message if no failed handler is set..
42519             if (!this.hasListener('actionfailed')) {
42520                 Roo.MessageBox.alert("Error",
42521                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
42522                         action.result.errorMsg :
42523                         "Saving Failed, please check your entries or try again"
42524                 );
42525             }
42526             
42527             this.fireEvent('actionfailed', this, action);
42528         }
42529         
42530     },
42531
42532     /**
42533      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
42534      * @param {String} id The value to search for
42535      * @return Field
42536      */
42537     findField : function(id){
42538         var field = this.items.get(id);
42539         if(!field){
42540             this.items.each(function(f){
42541                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
42542                     field = f;
42543                     return false;
42544                 }
42545             });
42546         }
42547         return field || null;
42548     },
42549
42550     /**
42551      * Add a secondary form to this one, 
42552      * Used to provide tabbed forms. One form is primary, with hidden values 
42553      * which mirror the elements from the other forms.
42554      * 
42555      * @param {Roo.form.Form} form to add.
42556      * 
42557      */
42558     addForm : function(form)
42559     {
42560        
42561         if (this.childForms.indexOf(form) > -1) {
42562             // already added..
42563             return;
42564         }
42565         this.childForms.push(form);
42566         var n = '';
42567         Roo.each(form.allItems, function (fe) {
42568             
42569             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
42570             if (this.findField(n)) { // already added..
42571                 return;
42572             }
42573             var add = new Roo.form.Hidden({
42574                 name : n
42575             });
42576             add.render(this.el);
42577             
42578             this.add( add );
42579         }, this);
42580         
42581     },
42582     /**
42583      * Mark fields in this form invalid in bulk.
42584      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
42585      * @return {BasicForm} this
42586      */
42587     markInvalid : function(errors){
42588         if(errors instanceof Array){
42589             for(var i = 0, len = errors.length; i < len; i++){
42590                 var fieldError = errors[i];
42591                 var f = this.findField(fieldError.id);
42592                 if(f){
42593                     f.markInvalid(fieldError.msg);
42594                 }
42595             }
42596         }else{
42597             var field, id;
42598             for(id in errors){
42599                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
42600                     field.markInvalid(errors[id]);
42601                 }
42602             }
42603         }
42604         Roo.each(this.childForms || [], function (f) {
42605             f.markInvalid(errors);
42606         });
42607         
42608         return this;
42609     },
42610
42611     /**
42612      * Set values for fields in this form in bulk.
42613      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
42614      * @return {BasicForm} this
42615      */
42616     setValues : function(values){
42617         if(values instanceof Array){ // array of objects
42618             for(var i = 0, len = values.length; i < len; i++){
42619                 var v = values[i];
42620                 var f = this.findField(v.id);
42621                 if(f){
42622                     f.setValue(v.value);
42623                     if(this.trackResetOnLoad){
42624                         f.originalValue = f.getValue();
42625                     }
42626                 }
42627             }
42628         }else{ // object hash
42629             var field, id;
42630             for(id in values){
42631                 if(typeof values[id] != 'function' && (field = this.findField(id))){
42632                     
42633                     if (field.setFromData && 
42634                         field.valueField && 
42635                         field.displayField &&
42636                         // combos' with local stores can 
42637                         // be queried via setValue()
42638                         // to set their value..
42639                         (field.store && !field.store.isLocal)
42640                         ) {
42641                         // it's a combo
42642                         var sd = { };
42643                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
42644                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
42645                         field.setFromData(sd);
42646                         
42647                     } else {
42648                         field.setValue(values[id]);
42649                     }
42650                     
42651                     
42652                     if(this.trackResetOnLoad){
42653                         field.originalValue = field.getValue();
42654                     }
42655                 }
42656             }
42657         }
42658          
42659         Roo.each(this.childForms || [], function (f) {
42660             f.setValues(values);
42661         });
42662                 
42663         return this;
42664     },
42665
42666     /**
42667      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
42668      * they are returned as an array.
42669      * @param {Boolean} asString
42670      * @return {Object}
42671      */
42672     getValues : function(asString){
42673         if (this.childForms) {
42674             // copy values from the child forms
42675             Roo.each(this.childForms, function (f) {
42676                 this.setValues(f.getValues());
42677             }, this);
42678         }
42679         
42680         
42681         
42682         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
42683         if(asString === true){
42684             return fs;
42685         }
42686         return Roo.urlDecode(fs);
42687     },
42688     
42689     /**
42690      * Returns the fields in this form as an object with key/value pairs. 
42691      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
42692      * @return {Object}
42693      */
42694     getFieldValues : function(with_hidden)
42695     {
42696         if (this.childForms) {
42697             // copy values from the child forms
42698             // should this call getFieldValues - probably not as we do not currently copy
42699             // hidden fields when we generate..
42700             Roo.each(this.childForms, function (f) {
42701                 this.setValues(f.getValues());
42702             }, this);
42703         }
42704         
42705         var ret = {};
42706         this.items.each(function(f){
42707             if (!f.getName()) {
42708                 return;
42709             }
42710             var v = f.getValue();
42711             // not sure if this supported any more..
42712             if ((typeof(v) == 'object') && f.getRawValue) {
42713                 v = f.getRawValue() ; // dates..
42714             }
42715             // combo boxes where name != hiddenName...
42716             if (f.name != f.getName()) {
42717                 ret[f.name] = f.getRawValue();
42718             }
42719             ret[f.getName()] = v;
42720         });
42721         
42722         return ret;
42723     },
42724
42725     /**
42726      * Clears all invalid messages in this form.
42727      * @return {BasicForm} this
42728      */
42729     clearInvalid : function(){
42730         this.items.each(function(f){
42731            f.clearInvalid();
42732         });
42733         
42734         Roo.each(this.childForms || [], function (f) {
42735             f.clearInvalid();
42736         });
42737         
42738         
42739         return this;
42740     },
42741
42742     /**
42743      * Resets this form.
42744      * @return {BasicForm} this
42745      */
42746     reset : function(){
42747         this.items.each(function(f){
42748             f.reset();
42749         });
42750         
42751         Roo.each(this.childForms || [], function (f) {
42752             f.reset();
42753         });
42754        
42755         
42756         return this;
42757     },
42758
42759     /**
42760      * Add Roo.form components to this form.
42761      * @param {Field} field1
42762      * @param {Field} field2 (optional)
42763      * @param {Field} etc (optional)
42764      * @return {BasicForm} this
42765      */
42766     add : function(){
42767         this.items.addAll(Array.prototype.slice.call(arguments, 0));
42768         return this;
42769     },
42770
42771
42772     /**
42773      * Removes a field from the items collection (does NOT remove its markup).
42774      * @param {Field} field
42775      * @return {BasicForm} this
42776      */
42777     remove : function(field){
42778         this.items.remove(field);
42779         return this;
42780     },
42781
42782     /**
42783      * Looks at the fields in this form, checks them for an id attribute,
42784      * and calls applyTo on the existing dom element with that id.
42785      * @return {BasicForm} this
42786      */
42787     render : function(){
42788         this.items.each(function(f){
42789             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
42790                 f.applyTo(f.id);
42791             }
42792         });
42793         return this;
42794     },
42795
42796     /**
42797      * Calls {@link Ext#apply} for all fields in this form with the passed object.
42798      * @param {Object} values
42799      * @return {BasicForm} this
42800      */
42801     applyToFields : function(o){
42802         this.items.each(function(f){
42803            Roo.apply(f, o);
42804         });
42805         return this;
42806     },
42807
42808     /**
42809      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
42810      * @param {Object} values
42811      * @return {BasicForm} this
42812      */
42813     applyIfToFields : function(o){
42814         this.items.each(function(f){
42815            Roo.applyIf(f, o);
42816         });
42817         return this;
42818     }
42819 });
42820
42821 // back compat
42822 Roo.BasicForm = Roo.form.BasicForm;/*
42823  * Based on:
42824  * Ext JS Library 1.1.1
42825  * Copyright(c) 2006-2007, Ext JS, LLC.
42826  *
42827  * Originally Released Under LGPL - original licence link has changed is not relivant.
42828  *
42829  * Fork - LGPL
42830  * <script type="text/javascript">
42831  */
42832
42833 /**
42834  * @class Roo.form.Form
42835  * @extends Roo.form.BasicForm
42836  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
42837  * @constructor
42838  * @param {Object} config Configuration options
42839  */
42840 Roo.form.Form = function(config){
42841     var xitems =  [];
42842     if (config.items) {
42843         xitems = config.items;
42844         delete config.items;
42845     }
42846    
42847     
42848     Roo.form.Form.superclass.constructor.call(this, null, config);
42849     this.url = this.url || this.action;
42850     if(!this.root){
42851         this.root = new Roo.form.Layout(Roo.applyIf({
42852             id: Roo.id()
42853         }, config));
42854     }
42855     this.active = this.root;
42856     /**
42857      * Array of all the buttons that have been added to this form via {@link addButton}
42858      * @type Array
42859      */
42860     this.buttons = [];
42861     this.allItems = [];
42862     this.addEvents({
42863         /**
42864          * @event clientvalidation
42865          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
42866          * @param {Form} this
42867          * @param {Boolean} valid true if the form has passed client-side validation
42868          */
42869         clientvalidation: true,
42870         /**
42871          * @event rendered
42872          * Fires when the form is rendered
42873          * @param {Roo.form.Form} form
42874          */
42875         rendered : true
42876     });
42877     
42878     if (this.progressUrl) {
42879             // push a hidden field onto the list of fields..
42880             this.addxtype( {
42881                     xns: Roo.form, 
42882                     xtype : 'Hidden', 
42883                     name : 'UPLOAD_IDENTIFIER' 
42884             });
42885         }
42886         
42887     
42888     Roo.each(xitems, this.addxtype, this);
42889     
42890     
42891     
42892 };
42893
42894 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
42895     /**
42896      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
42897      */
42898     /**
42899      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
42900      */
42901     /**
42902      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
42903      */
42904     buttonAlign:'center',
42905
42906     /**
42907      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
42908      */
42909     minButtonWidth:75,
42910
42911     /**
42912      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
42913      * This property cascades to child containers if not set.
42914      */
42915     labelAlign:'left',
42916
42917     /**
42918      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
42919      * fires a looping event with that state. This is required to bind buttons to the valid
42920      * state using the config value formBind:true on the button.
42921      */
42922     monitorValid : false,
42923
42924     /**
42925      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
42926      */
42927     monitorPoll : 200,
42928     
42929     /**
42930      * @cfg {String} progressUrl - Url to return progress data 
42931      */
42932     
42933     progressUrl : false,
42934   
42935     /**
42936      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
42937      * fields are added and the column is closed. If no fields are passed the column remains open
42938      * until end() is called.
42939      * @param {Object} config The config to pass to the column
42940      * @param {Field} field1 (optional)
42941      * @param {Field} field2 (optional)
42942      * @param {Field} etc (optional)
42943      * @return Column The column container object
42944      */
42945     column : function(c){
42946         var col = new Roo.form.Column(c);
42947         this.start(col);
42948         if(arguments.length > 1){ // duplicate code required because of Opera
42949             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42950             this.end();
42951         }
42952         return col;
42953     },
42954
42955     /**
42956      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
42957      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
42958      * until end() is called.
42959      * @param {Object} config The config to pass to the fieldset
42960      * @param {Field} field1 (optional)
42961      * @param {Field} field2 (optional)
42962      * @param {Field} etc (optional)
42963      * @return FieldSet The fieldset container object
42964      */
42965     fieldset : function(c){
42966         var fs = new Roo.form.FieldSet(c);
42967         this.start(fs);
42968         if(arguments.length > 1){ // duplicate code required because of Opera
42969             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42970             this.end();
42971         }
42972         return fs;
42973     },
42974
42975     /**
42976      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
42977      * fields are added and the container is closed. If no fields are passed the container remains open
42978      * until end() is called.
42979      * @param {Object} config The config to pass to the Layout
42980      * @param {Field} field1 (optional)
42981      * @param {Field} field2 (optional)
42982      * @param {Field} etc (optional)
42983      * @return Layout The container object
42984      */
42985     container : function(c){
42986         var l = new Roo.form.Layout(c);
42987         this.start(l);
42988         if(arguments.length > 1){ // duplicate code required because of Opera
42989             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42990             this.end();
42991         }
42992         return l;
42993     },
42994
42995     /**
42996      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
42997      * @param {Object} container A Roo.form.Layout or subclass of Layout
42998      * @return {Form} this
42999      */
43000     start : function(c){
43001         // cascade label info
43002         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
43003         this.active.stack.push(c);
43004         c.ownerCt = this.active;
43005         this.active = c;
43006         return this;
43007     },
43008
43009     /**
43010      * Closes the current open container
43011      * @return {Form} this
43012      */
43013     end : function(){
43014         if(this.active == this.root){
43015             return this;
43016         }
43017         this.active = this.active.ownerCt;
43018         return this;
43019     },
43020
43021     /**
43022      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
43023      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
43024      * as the label of the field.
43025      * @param {Field} field1
43026      * @param {Field} field2 (optional)
43027      * @param {Field} etc. (optional)
43028      * @return {Form} this
43029      */
43030     add : function(){
43031         this.active.stack.push.apply(this.active.stack, arguments);
43032         this.allItems.push.apply(this.allItems,arguments);
43033         var r = [];
43034         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
43035             if(a[i].isFormField){
43036                 r.push(a[i]);
43037             }
43038         }
43039         if(r.length > 0){
43040             Roo.form.Form.superclass.add.apply(this, r);
43041         }
43042         return this;
43043     },
43044     
43045
43046     
43047     
43048     
43049      /**
43050      * Find any element that has been added to a form, using it's ID or name
43051      * This can include framesets, columns etc. along with regular fields..
43052      * @param {String} id - id or name to find.
43053      
43054      * @return {Element} e - or false if nothing found.
43055      */
43056     findbyId : function(id)
43057     {
43058         var ret = false;
43059         if (!id) {
43060             return ret;
43061         }
43062         Roo.each(this.allItems, function(f){
43063             if (f.id == id || f.name == id ){
43064                 ret = f;
43065                 return false;
43066             }
43067         });
43068         return ret;
43069     },
43070
43071     
43072     
43073     /**
43074      * Render this form into the passed container. This should only be called once!
43075      * @param {String/HTMLElement/Element} container The element this component should be rendered into
43076      * @return {Form} this
43077      */
43078     render : function(ct)
43079     {
43080         
43081         
43082         
43083         ct = Roo.get(ct);
43084         var o = this.autoCreate || {
43085             tag: 'form',
43086             method : this.method || 'POST',
43087             id : this.id || Roo.id()
43088         };
43089         this.initEl(ct.createChild(o));
43090
43091         this.root.render(this.el);
43092         
43093        
43094              
43095         this.items.each(function(f){
43096             f.render('x-form-el-'+f.id);
43097         });
43098
43099         if(this.buttons.length > 0){
43100             // tables are required to maintain order and for correct IE layout
43101             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
43102                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
43103                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
43104             }}, null, true);
43105             var tr = tb.getElementsByTagName('tr')[0];
43106             for(var i = 0, len = this.buttons.length; i < len; i++) {
43107                 var b = this.buttons[i];
43108                 var td = document.createElement('td');
43109                 td.className = 'x-form-btn-td';
43110                 b.render(tr.appendChild(td));
43111             }
43112         }
43113         if(this.monitorValid){ // initialize after render
43114             this.startMonitoring();
43115         }
43116         this.fireEvent('rendered', this);
43117         return this;
43118     },
43119
43120     /**
43121      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
43122      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
43123      * object or a valid Roo.DomHelper element config
43124      * @param {Function} handler The function called when the button is clicked
43125      * @param {Object} scope (optional) The scope of the handler function
43126      * @return {Roo.Button}
43127      */
43128     addButton : function(config, handler, scope){
43129         var bc = {
43130             handler: handler,
43131             scope: scope,
43132             minWidth: this.minButtonWidth,
43133             hideParent:true
43134         };
43135         if(typeof config == "string"){
43136             bc.text = config;
43137         }else{
43138             Roo.apply(bc, config);
43139         }
43140         var btn = new Roo.Button(null, bc);
43141         this.buttons.push(btn);
43142         return btn;
43143     },
43144
43145      /**
43146      * Adds a series of form elements (using the xtype property as the factory method.
43147      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
43148      * @param {Object} config 
43149      */
43150     
43151     addxtype : function()
43152     {
43153         var ar = Array.prototype.slice.call(arguments, 0);
43154         var ret = false;
43155         for(var i = 0; i < ar.length; i++) {
43156             if (!ar[i]) {
43157                 continue; // skip -- if this happends something invalid got sent, we 
43158                 // should ignore it, as basically that interface element will not show up
43159                 // and that should be pretty obvious!!
43160             }
43161             
43162             if (Roo.form[ar[i].xtype]) {
43163                 ar[i].form = this;
43164                 var fe = Roo.factory(ar[i], Roo.form);
43165                 if (!ret) {
43166                     ret = fe;
43167                 }
43168                 fe.form = this;
43169                 if (fe.store) {
43170                     fe.store.form = this;
43171                 }
43172                 if (fe.isLayout) {  
43173                          
43174                     this.start(fe);
43175                     this.allItems.push(fe);
43176                     if (fe.items && fe.addxtype) {
43177                         fe.addxtype.apply(fe, fe.items);
43178                         delete fe.items;
43179                     }
43180                      this.end();
43181                     continue;
43182                 }
43183                 
43184                 
43185                  
43186                 this.add(fe);
43187               //  console.log('adding ' + ar[i].xtype);
43188             }
43189             if (ar[i].xtype == 'Button') {  
43190                 //console.log('adding button');
43191                 //console.log(ar[i]);
43192                 this.addButton(ar[i]);
43193                 this.allItems.push(fe);
43194                 continue;
43195             }
43196             
43197             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
43198                 alert('end is not supported on xtype any more, use items');
43199             //    this.end();
43200             //    //console.log('adding end');
43201             }
43202             
43203         }
43204         return ret;
43205     },
43206     
43207     /**
43208      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
43209      * option "monitorValid"
43210      */
43211     startMonitoring : function(){
43212         if(!this.bound){
43213             this.bound = true;
43214             Roo.TaskMgr.start({
43215                 run : this.bindHandler,
43216                 interval : this.monitorPoll || 200,
43217                 scope: this
43218             });
43219         }
43220     },
43221
43222     /**
43223      * Stops monitoring of the valid state of this form
43224      */
43225     stopMonitoring : function(){
43226         this.bound = false;
43227     },
43228
43229     // private
43230     bindHandler : function(){
43231         if(!this.bound){
43232             return false; // stops binding
43233         }
43234         var valid = true;
43235         this.items.each(function(f){
43236             if(!f.isValid(true)){
43237                 valid = false;
43238                 return false;
43239             }
43240         });
43241         for(var i = 0, len = this.buttons.length; i < len; i++){
43242             var btn = this.buttons[i];
43243             if(btn.formBind === true && btn.disabled === valid){
43244                 btn.setDisabled(!valid);
43245             }
43246         }
43247         this.fireEvent('clientvalidation', this, valid);
43248     }
43249     
43250     
43251     
43252     
43253     
43254     
43255     
43256     
43257 });
43258
43259
43260 // back compat
43261 Roo.Form = Roo.form.Form;
43262 /*
43263  * Based on:
43264  * Ext JS Library 1.1.1
43265  * Copyright(c) 2006-2007, Ext JS, LLC.
43266  *
43267  * Originally Released Under LGPL - original licence link has changed is not relivant.
43268  *
43269  * Fork - LGPL
43270  * <script type="text/javascript">
43271  */
43272  
43273  /**
43274  * @class Roo.form.Action
43275  * Internal Class used to handle form actions
43276  * @constructor
43277  * @param {Roo.form.BasicForm} el The form element or its id
43278  * @param {Object} config Configuration options
43279  */
43280  
43281  
43282 // define the action interface
43283 Roo.form.Action = function(form, options){
43284     this.form = form;
43285     this.options = options || {};
43286 };
43287 /**
43288  * Client Validation Failed
43289  * @const 
43290  */
43291 Roo.form.Action.CLIENT_INVALID = 'client';
43292 /**
43293  * Server Validation Failed
43294  * @const 
43295  */
43296  Roo.form.Action.SERVER_INVALID = 'server';
43297  /**
43298  * Connect to Server Failed
43299  * @const 
43300  */
43301 Roo.form.Action.CONNECT_FAILURE = 'connect';
43302 /**
43303  * Reading Data from Server Failed
43304  * @const 
43305  */
43306 Roo.form.Action.LOAD_FAILURE = 'load';
43307
43308 Roo.form.Action.prototype = {
43309     type : 'default',
43310     failureType : undefined,
43311     response : undefined,
43312     result : undefined,
43313
43314     // interface method
43315     run : function(options){
43316
43317     },
43318
43319     // interface method
43320     success : function(response){
43321
43322     },
43323
43324     // interface method
43325     handleResponse : function(response){
43326
43327     },
43328
43329     // default connection failure
43330     failure : function(response){
43331         
43332         this.response = response;
43333         this.failureType = Roo.form.Action.CONNECT_FAILURE;
43334         this.form.afterAction(this, false);
43335     },
43336
43337     processResponse : function(response){
43338         this.response = response;
43339         if(!response.responseText){
43340             return true;
43341         }
43342         this.result = this.handleResponse(response);
43343         return this.result;
43344     },
43345
43346     // utility functions used internally
43347     getUrl : function(appendParams){
43348         var url = this.options.url || this.form.url || this.form.el.dom.action;
43349         if(appendParams){
43350             var p = this.getParams();
43351             if(p){
43352                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
43353             }
43354         }
43355         return url;
43356     },
43357
43358     getMethod : function(){
43359         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
43360     },
43361
43362     getParams : function(){
43363         var bp = this.form.baseParams;
43364         var p = this.options.params;
43365         if(p){
43366             if(typeof p == "object"){
43367                 p = Roo.urlEncode(Roo.applyIf(p, bp));
43368             }else if(typeof p == 'string' && bp){
43369                 p += '&' + Roo.urlEncode(bp);
43370             }
43371         }else if(bp){
43372             p = Roo.urlEncode(bp);
43373         }
43374         return p;
43375     },
43376
43377     createCallback : function(){
43378         return {
43379             success: this.success,
43380             failure: this.failure,
43381             scope: this,
43382             timeout: (this.form.timeout*1000),
43383             upload: this.form.fileUpload ? this.success : undefined
43384         };
43385     }
43386 };
43387
43388 Roo.form.Action.Submit = function(form, options){
43389     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
43390 };
43391
43392 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
43393     type : 'submit',
43394
43395     haveProgress : false,
43396     uploadComplete : false,
43397     
43398     // uploadProgress indicator.
43399     uploadProgress : function()
43400     {
43401         if (!this.form.progressUrl) {
43402             return;
43403         }
43404         
43405         if (!this.haveProgress) {
43406             Roo.MessageBox.progress("Uploading", "Uploading");
43407         }
43408         if (this.uploadComplete) {
43409            Roo.MessageBox.hide();
43410            return;
43411         }
43412         
43413         this.haveProgress = true;
43414    
43415         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
43416         
43417         var c = new Roo.data.Connection();
43418         c.request({
43419             url : this.form.progressUrl,
43420             params: {
43421                 id : uid
43422             },
43423             method: 'GET',
43424             success : function(req){
43425                //console.log(data);
43426                 var rdata = false;
43427                 var edata;
43428                 try  {
43429                    rdata = Roo.decode(req.responseText)
43430                 } catch (e) {
43431                     Roo.log("Invalid data from server..");
43432                     Roo.log(edata);
43433                     return;
43434                 }
43435                 if (!rdata || !rdata.success) {
43436                     Roo.log(rdata);
43437                     return;
43438                 }
43439                 var data = rdata.data;
43440                 
43441                 if (this.uploadComplete) {
43442                    Roo.MessageBox.hide();
43443                    return;
43444                 }
43445                    
43446                 if (data){
43447                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
43448                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
43449                     );
43450                 }
43451                 this.uploadProgress.defer(2000,this);
43452             },
43453        
43454             failure: function(data) {
43455                 Roo.log('progress url failed ');
43456                 Roo.log(data);
43457             },
43458             scope : this
43459         });
43460            
43461     },
43462     
43463     
43464     run : function()
43465     {
43466         // run get Values on the form, so it syncs any secondary forms.
43467         this.form.getValues();
43468         
43469         var o = this.options;
43470         var method = this.getMethod();
43471         var isPost = method == 'POST';
43472         if(o.clientValidation === false || this.form.isValid()){
43473             
43474             if (this.form.progressUrl) {
43475                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
43476                     (new Date() * 1) + '' + Math.random());
43477                     
43478             } 
43479             
43480             
43481             Roo.Ajax.request(Roo.apply(this.createCallback(), {
43482                 form:this.form.el.dom,
43483                 url:this.getUrl(!isPost),
43484                 method: method,
43485                 params:isPost ? this.getParams() : null,
43486                 isUpload: this.form.fileUpload
43487             }));
43488             
43489             this.uploadProgress();
43490
43491         }else if (o.clientValidation !== false){ // client validation failed
43492             this.failureType = Roo.form.Action.CLIENT_INVALID;
43493             this.form.afterAction(this, false);
43494         }
43495     },
43496
43497     success : function(response)
43498     {
43499         this.uploadComplete= true;
43500         if (this.haveProgress) {
43501             Roo.MessageBox.hide();
43502         }
43503         
43504         
43505         var result = this.processResponse(response);
43506         if(result === true || result.success){
43507             this.form.afterAction(this, true);
43508             return;
43509         }
43510         if(result.errors){
43511             this.form.markInvalid(result.errors);
43512             this.failureType = Roo.form.Action.SERVER_INVALID;
43513         }
43514         this.form.afterAction(this, false);
43515     },
43516     failure : function(response)
43517     {
43518         this.uploadComplete= true;
43519         if (this.haveProgress) {
43520             Roo.MessageBox.hide();
43521         }
43522         
43523         this.response = response;
43524         this.failureType = Roo.form.Action.CONNECT_FAILURE;
43525         this.form.afterAction(this, false);
43526     },
43527     
43528     handleResponse : function(response){
43529         if(this.form.errorReader){
43530             var rs = this.form.errorReader.read(response);
43531             var errors = [];
43532             if(rs.records){
43533                 for(var i = 0, len = rs.records.length; i < len; i++) {
43534                     var r = rs.records[i];
43535                     errors[i] = r.data;
43536                 }
43537             }
43538             if(errors.length < 1){
43539                 errors = null;
43540             }
43541             return {
43542                 success : rs.success,
43543                 errors : errors
43544             };
43545         }
43546         var ret = false;
43547         try {
43548             ret = Roo.decode(response.responseText);
43549         } catch (e) {
43550             ret = {
43551                 success: false,
43552                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
43553                 errors : []
43554             };
43555         }
43556         return ret;
43557         
43558     }
43559 });
43560
43561
43562 Roo.form.Action.Load = function(form, options){
43563     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
43564     this.reader = this.form.reader;
43565 };
43566
43567 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
43568     type : 'load',
43569
43570     run : function(){
43571         
43572         Roo.Ajax.request(Roo.apply(
43573                 this.createCallback(), {
43574                     method:this.getMethod(),
43575                     url:this.getUrl(false),
43576                     params:this.getParams()
43577         }));
43578     },
43579
43580     success : function(response){
43581         
43582         var result = this.processResponse(response);
43583         if(result === true || !result.success || !result.data){
43584             this.failureType = Roo.form.Action.LOAD_FAILURE;
43585             this.form.afterAction(this, false);
43586             return;
43587         }
43588         this.form.clearInvalid();
43589         this.form.setValues(result.data);
43590         this.form.afterAction(this, true);
43591     },
43592
43593     handleResponse : function(response){
43594         if(this.form.reader){
43595             var rs = this.form.reader.read(response);
43596             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
43597             return {
43598                 success : rs.success,
43599                 data : data
43600             };
43601         }
43602         return Roo.decode(response.responseText);
43603     }
43604 });
43605
43606 Roo.form.Action.ACTION_TYPES = {
43607     'load' : Roo.form.Action.Load,
43608     'submit' : Roo.form.Action.Submit
43609 };/*
43610  * Based on:
43611  * Ext JS Library 1.1.1
43612  * Copyright(c) 2006-2007, Ext JS, LLC.
43613  *
43614  * Originally Released Under LGPL - original licence link has changed is not relivant.
43615  *
43616  * Fork - LGPL
43617  * <script type="text/javascript">
43618  */
43619  
43620 /**
43621  * @class Roo.form.Layout
43622  * @extends Roo.Component
43623  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
43624  * @constructor
43625  * @param {Object} config Configuration options
43626  */
43627 Roo.form.Layout = function(config){
43628     var xitems = [];
43629     if (config.items) {
43630         xitems = config.items;
43631         delete config.items;
43632     }
43633     Roo.form.Layout.superclass.constructor.call(this, config);
43634     this.stack = [];
43635     Roo.each(xitems, this.addxtype, this);
43636      
43637 };
43638
43639 Roo.extend(Roo.form.Layout, Roo.Component, {
43640     /**
43641      * @cfg {String/Object} autoCreate
43642      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
43643      */
43644     /**
43645      * @cfg {String/Object/Function} style
43646      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
43647      * a function which returns such a specification.
43648      */
43649     /**
43650      * @cfg {String} labelAlign
43651      * Valid values are "left," "top" and "right" (defaults to "left")
43652      */
43653     /**
43654      * @cfg {Number} labelWidth
43655      * Fixed width in pixels of all field labels (defaults to undefined)
43656      */
43657     /**
43658      * @cfg {Boolean} clear
43659      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
43660      */
43661     clear : true,
43662     /**
43663      * @cfg {String} labelSeparator
43664      * The separator to use after field labels (defaults to ':')
43665      */
43666     labelSeparator : ':',
43667     /**
43668      * @cfg {Boolean} hideLabels
43669      * True to suppress the display of field labels in this layout (defaults to false)
43670      */
43671     hideLabels : false,
43672
43673     // private
43674     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
43675     
43676     isLayout : true,
43677     
43678     // private
43679     onRender : function(ct, position){
43680         if(this.el){ // from markup
43681             this.el = Roo.get(this.el);
43682         }else {  // generate
43683             var cfg = this.getAutoCreate();
43684             this.el = ct.createChild(cfg, position);
43685         }
43686         if(this.style){
43687             this.el.applyStyles(this.style);
43688         }
43689         if(this.labelAlign){
43690             this.el.addClass('x-form-label-'+this.labelAlign);
43691         }
43692         if(this.hideLabels){
43693             this.labelStyle = "display:none";
43694             this.elementStyle = "padding-left:0;";
43695         }else{
43696             if(typeof this.labelWidth == 'number'){
43697                 this.labelStyle = "width:"+this.labelWidth+"px;";
43698                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
43699             }
43700             if(this.labelAlign == 'top'){
43701                 this.labelStyle = "width:auto;";
43702                 this.elementStyle = "padding-left:0;";
43703             }
43704         }
43705         var stack = this.stack;
43706         var slen = stack.length;
43707         if(slen > 0){
43708             if(!this.fieldTpl){
43709                 var t = new Roo.Template(
43710                     '<div class="x-form-item {5}">',
43711                         '<label for="{0}" style="{2}">{1}{4}</label>',
43712                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
43713                         '</div>',
43714                     '</div><div class="x-form-clear-left"></div>'
43715                 );
43716                 t.disableFormats = true;
43717                 t.compile();
43718                 Roo.form.Layout.prototype.fieldTpl = t;
43719             }
43720             for(var i = 0; i < slen; i++) {
43721                 if(stack[i].isFormField){
43722                     this.renderField(stack[i]);
43723                 }else{
43724                     this.renderComponent(stack[i]);
43725                 }
43726             }
43727         }
43728         if(this.clear){
43729             this.el.createChild({cls:'x-form-clear'});
43730         }
43731     },
43732
43733     // private
43734     renderField : function(f){
43735         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
43736                f.id, //0
43737                f.fieldLabel, //1
43738                f.labelStyle||this.labelStyle||'', //2
43739                this.elementStyle||'', //3
43740                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
43741                f.itemCls||this.itemCls||''  //5
43742        ], true).getPrevSibling());
43743     },
43744
43745     // private
43746     renderComponent : function(c){
43747         c.render(c.isLayout ? this.el : this.el.createChild());    
43748     },
43749     /**
43750      * Adds a object form elements (using the xtype property as the factory method.)
43751      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
43752      * @param {Object} config 
43753      */
43754     addxtype : function(o)
43755     {
43756         // create the lement.
43757         o.form = this.form;
43758         var fe = Roo.factory(o, Roo.form);
43759         this.form.allItems.push(fe);
43760         this.stack.push(fe);
43761         
43762         if (fe.isFormField) {
43763             this.form.items.add(fe);
43764         }
43765          
43766         return fe;
43767     }
43768 });
43769
43770 /**
43771  * @class Roo.form.Column
43772  * @extends Roo.form.Layout
43773  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
43774  * @constructor
43775  * @param {Object} config Configuration options
43776  */
43777 Roo.form.Column = function(config){
43778     Roo.form.Column.superclass.constructor.call(this, config);
43779 };
43780
43781 Roo.extend(Roo.form.Column, Roo.form.Layout, {
43782     /**
43783      * @cfg {Number/String} width
43784      * The fixed width of the column in pixels or CSS value (defaults to "auto")
43785      */
43786     /**
43787      * @cfg {String/Object} autoCreate
43788      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
43789      */
43790
43791     // private
43792     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
43793
43794     // private
43795     onRender : function(ct, position){
43796         Roo.form.Column.superclass.onRender.call(this, ct, position);
43797         if(this.width){
43798             this.el.setWidth(this.width);
43799         }
43800     }
43801 });
43802
43803
43804 /**
43805  * @class Roo.form.Row
43806  * @extends Roo.form.Layout
43807  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
43808  * @constructor
43809  * @param {Object} config Configuration options
43810  */
43811
43812  
43813 Roo.form.Row = function(config){
43814     Roo.form.Row.superclass.constructor.call(this, config);
43815 };
43816  
43817 Roo.extend(Roo.form.Row, Roo.form.Layout, {
43818       /**
43819      * @cfg {Number/String} width
43820      * The fixed width of the column in pixels or CSS value (defaults to "auto")
43821      */
43822     /**
43823      * @cfg {Number/String} height
43824      * The fixed height of the column in pixels or CSS value (defaults to "auto")
43825      */
43826     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
43827     
43828     padWidth : 20,
43829     // private
43830     onRender : function(ct, position){
43831         //console.log('row render');
43832         if(!this.rowTpl){
43833             var t = new Roo.Template(
43834                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
43835                     '<label for="{0}" style="{2}">{1}{4}</label>',
43836                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
43837                     '</div>',
43838                 '</div>'
43839             );
43840             t.disableFormats = true;
43841             t.compile();
43842             Roo.form.Layout.prototype.rowTpl = t;
43843         }
43844         this.fieldTpl = this.rowTpl;
43845         
43846         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
43847         var labelWidth = 100;
43848         
43849         if ((this.labelAlign != 'top')) {
43850             if (typeof this.labelWidth == 'number') {
43851                 labelWidth = this.labelWidth
43852             }
43853             this.padWidth =  20 + labelWidth;
43854             
43855         }
43856         
43857         Roo.form.Column.superclass.onRender.call(this, ct, position);
43858         if(this.width){
43859             this.el.setWidth(this.width);
43860         }
43861         if(this.height){
43862             this.el.setHeight(this.height);
43863         }
43864     },
43865     
43866     // private
43867     renderField : function(f){
43868         f.fieldEl = this.fieldTpl.append(this.el, [
43869                f.id, f.fieldLabel,
43870                f.labelStyle||this.labelStyle||'',
43871                this.elementStyle||'',
43872                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
43873                f.itemCls||this.itemCls||'',
43874                f.width ? f.width + this.padWidth : 160 + this.padWidth
43875        ],true);
43876     }
43877 });
43878  
43879
43880 /**
43881  * @class Roo.form.FieldSet
43882  * @extends Roo.form.Layout
43883  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
43884  * @constructor
43885  * @param {Object} config Configuration options
43886  */
43887 Roo.form.FieldSet = function(config){
43888     Roo.form.FieldSet.superclass.constructor.call(this, config);
43889 };
43890
43891 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
43892     /**
43893      * @cfg {String} legend
43894      * The text to display as the legend for the FieldSet (defaults to '')
43895      */
43896     /**
43897      * @cfg {String/Object} autoCreate
43898      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
43899      */
43900
43901     // private
43902     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
43903
43904     // private
43905     onRender : function(ct, position){
43906         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
43907         if(this.legend){
43908             this.setLegend(this.legend);
43909         }
43910     },
43911
43912     // private
43913     setLegend : function(text){
43914         if(this.rendered){
43915             this.el.child('legend').update(text);
43916         }
43917     }
43918 });/*
43919  * Based on:
43920  * Ext JS Library 1.1.1
43921  * Copyright(c) 2006-2007, Ext JS, LLC.
43922  *
43923  * Originally Released Under LGPL - original licence link has changed is not relivant.
43924  *
43925  * Fork - LGPL
43926  * <script type="text/javascript">
43927  */
43928 /**
43929  * @class Roo.form.VTypes
43930  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
43931  * @singleton
43932  */
43933 Roo.form.VTypes = function(){
43934     // closure these in so they are only created once.
43935     var alpha = /^[a-zA-Z_]+$/;
43936     var alphanum = /^[a-zA-Z0-9_]+$/;
43937     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
43938     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
43939
43940     // All these messages and functions are configurable
43941     return {
43942         /**
43943          * The function used to validate email addresses
43944          * @param {String} value The email address
43945          */
43946         'email' : function(v){
43947             return email.test(v);
43948         },
43949         /**
43950          * The error text to display when the email validation function returns false
43951          * @type String
43952          */
43953         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
43954         /**
43955          * The keystroke filter mask to be applied on email input
43956          * @type RegExp
43957          */
43958         'emailMask' : /[a-z0-9_\.\-@]/i,
43959
43960         /**
43961          * The function used to validate URLs
43962          * @param {String} value The URL
43963          */
43964         'url' : function(v){
43965             return url.test(v);
43966         },
43967         /**
43968          * The error text to display when the url validation function returns false
43969          * @type String
43970          */
43971         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
43972         
43973         /**
43974          * The function used to validate alpha values
43975          * @param {String} value The value
43976          */
43977         'alpha' : function(v){
43978             return alpha.test(v);
43979         },
43980         /**
43981          * The error text to display when the alpha validation function returns false
43982          * @type String
43983          */
43984         'alphaText' : 'This field should only contain letters and _',
43985         /**
43986          * The keystroke filter mask to be applied on alpha input
43987          * @type RegExp
43988          */
43989         'alphaMask' : /[a-z_]/i,
43990
43991         /**
43992          * The function used to validate alphanumeric values
43993          * @param {String} value The value
43994          */
43995         'alphanum' : function(v){
43996             return alphanum.test(v);
43997         },
43998         /**
43999          * The error text to display when the alphanumeric validation function returns false
44000          * @type String
44001          */
44002         'alphanumText' : 'This field should only contain letters, numbers and _',
44003         /**
44004          * The keystroke filter mask to be applied on alphanumeric input
44005          * @type RegExp
44006          */
44007         'alphanumMask' : /[a-z0-9_]/i
44008     };
44009 }();//<script type="text/javascript">
44010
44011 /**
44012  * @class Roo.form.FCKeditor
44013  * @extends Roo.form.TextArea
44014  * Wrapper around the FCKEditor http://www.fckeditor.net
44015  * @constructor
44016  * Creates a new FCKeditor
44017  * @param {Object} config Configuration options
44018  */
44019 Roo.form.FCKeditor = function(config){
44020     Roo.form.FCKeditor.superclass.constructor.call(this, config);
44021     this.addEvents({
44022          /**
44023          * @event editorinit
44024          * Fired when the editor is initialized - you can add extra handlers here..
44025          * @param {FCKeditor} this
44026          * @param {Object} the FCK object.
44027          */
44028         editorinit : true
44029     });
44030     
44031     
44032 };
44033 Roo.form.FCKeditor.editors = { };
44034 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
44035 {
44036     //defaultAutoCreate : {
44037     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
44038     //},
44039     // private
44040     /**
44041      * @cfg {Object} fck options - see fck manual for details.
44042      */
44043     fckconfig : false,
44044     
44045     /**
44046      * @cfg {Object} fck toolbar set (Basic or Default)
44047      */
44048     toolbarSet : 'Basic',
44049     /**
44050      * @cfg {Object} fck BasePath
44051      */ 
44052     basePath : '/fckeditor/',
44053     
44054     
44055     frame : false,
44056     
44057     value : '',
44058     
44059    
44060     onRender : function(ct, position)
44061     {
44062         if(!this.el){
44063             this.defaultAutoCreate = {
44064                 tag: "textarea",
44065                 style:"width:300px;height:60px;",
44066                 autocomplete: "off"
44067             };
44068         }
44069         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
44070         /*
44071         if(this.grow){
44072             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
44073             if(this.preventScrollbars){
44074                 this.el.setStyle("overflow", "hidden");
44075             }
44076             this.el.setHeight(this.growMin);
44077         }
44078         */
44079         //console.log('onrender' + this.getId() );
44080         Roo.form.FCKeditor.editors[this.getId()] = this;
44081          
44082
44083         this.replaceTextarea() ;
44084         
44085     },
44086     
44087     getEditor : function() {
44088         return this.fckEditor;
44089     },
44090     /**
44091      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
44092      * @param {Mixed} value The value to set
44093      */
44094     
44095     
44096     setValue : function(value)
44097     {
44098         //console.log('setValue: ' + value);
44099         
44100         if(typeof(value) == 'undefined') { // not sure why this is happending...
44101             return;
44102         }
44103         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44104         
44105         //if(!this.el || !this.getEditor()) {
44106         //    this.value = value;
44107             //this.setValue.defer(100,this,[value]);    
44108         //    return;
44109         //} 
44110         
44111         if(!this.getEditor()) {
44112             return;
44113         }
44114         
44115         this.getEditor().SetData(value);
44116         
44117         //
44118
44119     },
44120
44121     /**
44122      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
44123      * @return {Mixed} value The field value
44124      */
44125     getValue : function()
44126     {
44127         
44128         if (this.frame && this.frame.dom.style.display == 'none') {
44129             return Roo.form.FCKeditor.superclass.getValue.call(this);
44130         }
44131         
44132         if(!this.el || !this.getEditor()) {
44133            
44134            // this.getValue.defer(100,this); 
44135             return this.value;
44136         }
44137        
44138         
44139         var value=this.getEditor().GetData();
44140         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44141         return Roo.form.FCKeditor.superclass.getValue.call(this);
44142         
44143
44144     },
44145
44146     /**
44147      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
44148      * @return {Mixed} value The field value
44149      */
44150     getRawValue : function()
44151     {
44152         if (this.frame && this.frame.dom.style.display == 'none') {
44153             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44154         }
44155         
44156         if(!this.el || !this.getEditor()) {
44157             //this.getRawValue.defer(100,this); 
44158             return this.value;
44159             return;
44160         }
44161         
44162         
44163         
44164         var value=this.getEditor().GetData();
44165         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
44166         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44167          
44168     },
44169     
44170     setSize : function(w,h) {
44171         
44172         
44173         
44174         //if (this.frame && this.frame.dom.style.display == 'none') {
44175         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44176         //    return;
44177         //}
44178         //if(!this.el || !this.getEditor()) {
44179         //    this.setSize.defer(100,this, [w,h]); 
44180         //    return;
44181         //}
44182         
44183         
44184         
44185         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44186         
44187         this.frame.dom.setAttribute('width', w);
44188         this.frame.dom.setAttribute('height', h);
44189         this.frame.setSize(w,h);
44190         
44191     },
44192     
44193     toggleSourceEdit : function(value) {
44194         
44195       
44196          
44197         this.el.dom.style.display = value ? '' : 'none';
44198         this.frame.dom.style.display = value ?  'none' : '';
44199         
44200     },
44201     
44202     
44203     focus: function(tag)
44204     {
44205         if (this.frame.dom.style.display == 'none') {
44206             return Roo.form.FCKeditor.superclass.focus.call(this);
44207         }
44208         if(!this.el || !this.getEditor()) {
44209             this.focus.defer(100,this, [tag]); 
44210             return;
44211         }
44212         
44213         
44214         
44215         
44216         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
44217         this.getEditor().Focus();
44218         if (tgs.length) {
44219             if (!this.getEditor().Selection.GetSelection()) {
44220                 this.focus.defer(100,this, [tag]); 
44221                 return;
44222             }
44223             
44224             
44225             var r = this.getEditor().EditorDocument.createRange();
44226             r.setStart(tgs[0],0);
44227             r.setEnd(tgs[0],0);
44228             this.getEditor().Selection.GetSelection().removeAllRanges();
44229             this.getEditor().Selection.GetSelection().addRange(r);
44230             this.getEditor().Focus();
44231         }
44232         
44233     },
44234     
44235     
44236     
44237     replaceTextarea : function()
44238     {
44239         if ( document.getElementById( this.getId() + '___Frame' ) )
44240             return ;
44241         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
44242         //{
44243             // We must check the elements firstly using the Id and then the name.
44244         var oTextarea = document.getElementById( this.getId() );
44245         
44246         var colElementsByName = document.getElementsByName( this.getId() ) ;
44247          
44248         oTextarea.style.display = 'none' ;
44249
44250         if ( oTextarea.tabIndex ) {            
44251             this.TabIndex = oTextarea.tabIndex ;
44252         }
44253         
44254         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
44255         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
44256         this.frame = Roo.get(this.getId() + '___Frame')
44257     },
44258     
44259     _getConfigHtml : function()
44260     {
44261         var sConfig = '' ;
44262
44263         for ( var o in this.fckconfig ) {
44264             sConfig += sConfig.length > 0  ? '&amp;' : '';
44265             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
44266         }
44267
44268         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
44269     },
44270     
44271     
44272     _getIFrameHtml : function()
44273     {
44274         var sFile = 'fckeditor.html' ;
44275         /* no idea what this is about..
44276         try
44277         {
44278             if ( (/fcksource=true/i).test( window.top.location.search ) )
44279                 sFile = 'fckeditor.original.html' ;
44280         }
44281         catch (e) { 
44282         */
44283
44284         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
44285         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
44286         
44287         
44288         var html = '<iframe id="' + this.getId() +
44289             '___Frame" src="' + sLink +
44290             '" width="' + this.width +
44291             '" height="' + this.height + '"' +
44292             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
44293             ' frameborder="0" scrolling="no"></iframe>' ;
44294
44295         return html ;
44296     },
44297     
44298     _insertHtmlBefore : function( html, element )
44299     {
44300         if ( element.insertAdjacentHTML )       {
44301             // IE
44302             element.insertAdjacentHTML( 'beforeBegin', html ) ;
44303         } else { // Gecko
44304             var oRange = document.createRange() ;
44305             oRange.setStartBefore( element ) ;
44306             var oFragment = oRange.createContextualFragment( html );
44307             element.parentNode.insertBefore( oFragment, element ) ;
44308         }
44309     }
44310     
44311     
44312   
44313     
44314     
44315     
44316     
44317
44318 });
44319
44320 //Roo.reg('fckeditor', Roo.form.FCKeditor);
44321
44322 function FCKeditor_OnComplete(editorInstance){
44323     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
44324     f.fckEditor = editorInstance;
44325     //console.log("loaded");
44326     f.fireEvent('editorinit', f, editorInstance);
44327
44328   
44329
44330  
44331
44332
44333
44334
44335
44336
44337
44338
44339
44340
44341
44342
44343
44344
44345
44346 //<script type="text/javascript">
44347 /**
44348  * @class Roo.form.GridField
44349  * @extends Roo.form.Field
44350  * Embed a grid (or editable grid into a form)
44351  * STATUS ALPHA
44352  * 
44353  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
44354  * it needs 
44355  * xgrid.store = Roo.data.Store
44356  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
44357  * xgrid.store.reader = Roo.data.JsonReader 
44358  * 
44359  * 
44360  * @constructor
44361  * Creates a new GridField
44362  * @param {Object} config Configuration options
44363  */
44364 Roo.form.GridField = function(config){
44365     Roo.form.GridField.superclass.constructor.call(this, config);
44366      
44367 };
44368
44369 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
44370     /**
44371      * @cfg {Number} width  - used to restrict width of grid..
44372      */
44373     width : 100,
44374     /**
44375      * @cfg {Number} height - used to restrict height of grid..
44376      */
44377     height : 50,
44378      /**
44379      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
44380          * 
44381          *}
44382      */
44383     xgrid : false, 
44384     /**
44385      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44386      * {tag: "input", type: "checkbox", autocomplete: "off"})
44387      */
44388    // defaultAutoCreate : { tag: 'div' },
44389     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
44390     /**
44391      * @cfg {String} addTitle Text to include for adding a title.
44392      */
44393     addTitle : false,
44394     //
44395     onResize : function(){
44396         Roo.form.Field.superclass.onResize.apply(this, arguments);
44397     },
44398
44399     initEvents : function(){
44400         // Roo.form.Checkbox.superclass.initEvents.call(this);
44401         // has no events...
44402        
44403     },
44404
44405
44406     getResizeEl : function(){
44407         return this.wrap;
44408     },
44409
44410     getPositionEl : function(){
44411         return this.wrap;
44412     },
44413
44414     // private
44415     onRender : function(ct, position){
44416         
44417         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
44418         var style = this.style;
44419         delete this.style;
44420         
44421         Roo.form.GridField.superclass.onRender.call(this, ct, position);
44422         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
44423         this.viewEl = this.wrap.createChild({ tag: 'div' });
44424         if (style) {
44425             this.viewEl.applyStyles(style);
44426         }
44427         if (this.width) {
44428             this.viewEl.setWidth(this.width);
44429         }
44430         if (this.height) {
44431             this.viewEl.setHeight(this.height);
44432         }
44433         //if(this.inputValue !== undefined){
44434         //this.setValue(this.value);
44435         
44436         
44437         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
44438         
44439         
44440         this.grid.render();
44441         this.grid.getDataSource().on('remove', this.refreshValue, this);
44442         this.grid.getDataSource().on('update', this.refreshValue, this);
44443         this.grid.on('afteredit', this.refreshValue, this);
44444  
44445     },
44446      
44447     
44448     /**
44449      * Sets the value of the item. 
44450      * @param {String} either an object  or a string..
44451      */
44452     setValue : function(v){
44453         //this.value = v;
44454         v = v || []; // empty set..
44455         // this does not seem smart - it really only affects memoryproxy grids..
44456         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
44457             var ds = this.grid.getDataSource();
44458             // assumes a json reader..
44459             var data = {}
44460             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
44461             ds.loadData( data);
44462         }
44463         // clear selection so it does not get stale.
44464         if (this.grid.sm) { 
44465             this.grid.sm.clearSelections();
44466         }
44467         
44468         Roo.form.GridField.superclass.setValue.call(this, v);
44469         this.refreshValue();
44470         // should load data in the grid really....
44471     },
44472     
44473     // private
44474     refreshValue: function() {
44475          var val = [];
44476         this.grid.getDataSource().each(function(r) {
44477             val.push(r.data);
44478         });
44479         this.el.dom.value = Roo.encode(val);
44480     }
44481     
44482      
44483     
44484     
44485 });/*
44486  * Based on:
44487  * Ext JS Library 1.1.1
44488  * Copyright(c) 2006-2007, Ext JS, LLC.
44489  *
44490  * Originally Released Under LGPL - original licence link has changed is not relivant.
44491  *
44492  * Fork - LGPL
44493  * <script type="text/javascript">
44494  */
44495 /**
44496  * @class Roo.form.DisplayField
44497  * @extends Roo.form.Field
44498  * A generic Field to display non-editable data.
44499  * @constructor
44500  * Creates a new Display Field item.
44501  * @param {Object} config Configuration options
44502  */
44503 Roo.form.DisplayField = function(config){
44504     Roo.form.DisplayField.superclass.constructor.call(this, config);
44505     
44506 };
44507
44508 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
44509     inputType:      'hidden',
44510     allowBlank:     true,
44511     readOnly:         true,
44512     
44513  
44514     /**
44515      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44516      */
44517     focusClass : undefined,
44518     /**
44519      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44520      */
44521     fieldClass: 'x-form-field',
44522     
44523      /**
44524      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
44525      */
44526     valueRenderer: undefined,
44527     
44528     width: 100,
44529     /**
44530      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44531      * {tag: "input", type: "checkbox", autocomplete: "off"})
44532      */
44533      
44534  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
44535
44536     onResize : function(){
44537         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
44538         
44539     },
44540
44541     initEvents : function(){
44542         // Roo.form.Checkbox.superclass.initEvents.call(this);
44543         // has no events...
44544        
44545     },
44546
44547
44548     getResizeEl : function(){
44549         return this.wrap;
44550     },
44551
44552     getPositionEl : function(){
44553         return this.wrap;
44554     },
44555
44556     // private
44557     onRender : function(ct, position){
44558         
44559         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
44560         //if(this.inputValue !== undefined){
44561         this.wrap = this.el.wrap();
44562         
44563         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
44564         
44565         if (this.bodyStyle) {
44566             this.viewEl.applyStyles(this.bodyStyle);
44567         }
44568         //this.viewEl.setStyle('padding', '2px');
44569         
44570         this.setValue(this.value);
44571         
44572     },
44573 /*
44574     // private
44575     initValue : Roo.emptyFn,
44576
44577   */
44578
44579         // private
44580     onClick : function(){
44581         
44582     },
44583
44584     /**
44585      * Sets the checked state of the checkbox.
44586      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
44587      */
44588     setValue : function(v){
44589         this.value = v;
44590         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
44591         // this might be called before we have a dom element..
44592         if (!this.viewEl) {
44593             return;
44594         }
44595         this.viewEl.dom.innerHTML = html;
44596         Roo.form.DisplayField.superclass.setValue.call(this, v);
44597
44598     }
44599 });/*
44600  * 
44601  * Licence- LGPL
44602  * 
44603  */
44604
44605 /**
44606  * @class Roo.form.DayPicker
44607  * @extends Roo.form.Field
44608  * A Day picker show [M] [T] [W] ....
44609  * @constructor
44610  * Creates a new Day Picker
44611  * @param {Object} config Configuration options
44612  */
44613 Roo.form.DayPicker= function(config){
44614     Roo.form.DayPicker.superclass.constructor.call(this, config);
44615      
44616 };
44617
44618 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
44619     /**
44620      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44621      */
44622     focusClass : undefined,
44623     /**
44624      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44625      */
44626     fieldClass: "x-form-field",
44627    
44628     /**
44629      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44630      * {tag: "input", type: "checkbox", autocomplete: "off"})
44631      */
44632     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44633     
44634    
44635     actionMode : 'viewEl', 
44636     //
44637     // private
44638  
44639     inputType : 'hidden',
44640     
44641      
44642     inputElement: false, // real input element?
44643     basedOn: false, // ????
44644     
44645     isFormField: true, // not sure where this is needed!!!!
44646
44647     onResize : function(){
44648         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44649         if(!this.boxLabel){
44650             this.el.alignTo(this.wrap, 'c-c');
44651         }
44652     },
44653
44654     initEvents : function(){
44655         Roo.form.Checkbox.superclass.initEvents.call(this);
44656         this.el.on("click", this.onClick,  this);
44657         this.el.on("change", this.onClick,  this);
44658     },
44659
44660
44661     getResizeEl : function(){
44662         return this.wrap;
44663     },
44664
44665     getPositionEl : function(){
44666         return this.wrap;
44667     },
44668
44669     
44670     // private
44671     onRender : function(ct, position){
44672         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44673        
44674         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
44675         
44676         var r1 = '<table><tr>';
44677         var r2 = '<tr class="x-form-daypick-icons">';
44678         for (var i=0; i < 7; i++) {
44679             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
44680             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
44681         }
44682         
44683         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
44684         viewEl.select('img').on('click', this.onClick, this);
44685         this.viewEl = viewEl;   
44686         
44687         
44688         // this will not work on Chrome!!!
44689         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44690         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44691         
44692         
44693           
44694
44695     },
44696
44697     // private
44698     initValue : Roo.emptyFn,
44699
44700     /**
44701      * Returns the checked state of the checkbox.
44702      * @return {Boolean} True if checked, else false
44703      */
44704     getValue : function(){
44705         return this.el.dom.value;
44706         
44707     },
44708
44709         // private
44710     onClick : function(e){ 
44711         //this.setChecked(!this.checked);
44712         Roo.get(e.target).toggleClass('x-menu-item-checked');
44713         this.refreshValue();
44714         //if(this.el.dom.checked != this.checked){
44715         //    this.setValue(this.el.dom.checked);
44716        // }
44717     },
44718     
44719     // private
44720     refreshValue : function()
44721     {
44722         var val = '';
44723         this.viewEl.select('img',true).each(function(e,i,n)  {
44724             val += e.is(".x-menu-item-checked") ? String(n) : '';
44725         });
44726         this.setValue(val, true);
44727     },
44728
44729     /**
44730      * Sets the checked state of the checkbox.
44731      * On is always based on a string comparison between inputValue and the param.
44732      * @param {Boolean/String} value - the value to set 
44733      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44734      */
44735     setValue : function(v,suppressEvent){
44736         if (!this.el.dom) {
44737             return;
44738         }
44739         var old = this.el.dom.value ;
44740         this.el.dom.value = v;
44741         if (suppressEvent) {
44742             return ;
44743         }
44744          
44745         // update display..
44746         this.viewEl.select('img',true).each(function(e,i,n)  {
44747             
44748             var on = e.is(".x-menu-item-checked");
44749             var newv = v.indexOf(String(n)) > -1;
44750             if (on != newv) {
44751                 e.toggleClass('x-menu-item-checked');
44752             }
44753             
44754         });
44755         
44756         
44757         this.fireEvent('change', this, v, old);
44758         
44759         
44760     },
44761    
44762     // handle setting of hidden value by some other method!!?!?
44763     setFromHidden: function()
44764     {
44765         if(!this.el){
44766             return;
44767         }
44768         //console.log("SET FROM HIDDEN");
44769         //alert('setFrom hidden');
44770         this.setValue(this.el.dom.value);
44771     },
44772     
44773     onDestroy : function()
44774     {
44775         if(this.viewEl){
44776             Roo.get(this.viewEl).remove();
44777         }
44778          
44779         Roo.form.DayPicker.superclass.onDestroy.call(this);
44780     }
44781
44782 });/*
44783  * RooJS Library 1.1.1
44784  * Copyright(c) 2008-2011  Alan Knowles
44785  *
44786  * License - LGPL
44787  */
44788  
44789
44790 /**
44791  * @class Roo.form.ComboCheck
44792  * @extends Roo.form.ComboBox
44793  * A combobox for multiple select items.
44794  *
44795  * FIXME - could do with a reset button..
44796  * 
44797  * @constructor
44798  * Create a new ComboCheck
44799  * @param {Object} config Configuration options
44800  */
44801 Roo.form.ComboCheck = function(config){
44802     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44803     // should verify some data...
44804     // like
44805     // hiddenName = required..
44806     // displayField = required
44807     // valudField == required
44808     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44809     var _t = this;
44810     Roo.each(req, function(e) {
44811         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44812             throw "Roo.form.ComboCheck : missing value for: " + e;
44813         }
44814     });
44815     
44816     
44817 };
44818
44819 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
44820      
44821      
44822     editable : false,
44823      
44824     selectedClass: 'x-menu-item-checked', 
44825     
44826     // private
44827     onRender : function(ct, position){
44828         var _t = this;
44829         
44830         
44831         
44832         if(!this.tpl){
44833             var cls = 'x-combo-list';
44834
44835             
44836             this.tpl =  new Roo.Template({
44837                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
44838                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
44839                    '<span>{' + this.displayField + '}</span>' +
44840                     '</div>' 
44841                 
44842             });
44843         }
44844  
44845         
44846         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
44847         this.view.singleSelect = false;
44848         this.view.multiSelect = true;
44849         this.view.toggleSelect = true;
44850         this.pageTb.add(new Roo.Toolbar.Fill(), {
44851             
44852             text: 'Done',
44853             handler: function()
44854             {
44855                 _t.collapse();
44856             }
44857         });
44858     },
44859     
44860     onViewOver : function(e, t){
44861         // do nothing...
44862         return;
44863         
44864     },
44865     
44866     onViewClick : function(doFocus,index){
44867         return;
44868         
44869     },
44870     select: function () {
44871         //Roo.log("SELECT CALLED");
44872     },
44873      
44874     selectByValue : function(xv, scrollIntoView){
44875         var ar = this.getValueArray();
44876         var sels = [];
44877         
44878         Roo.each(ar, function(v) {
44879             if(v === undefined || v === null){
44880                 return;
44881             }
44882             var r = this.findRecord(this.valueField, v);
44883             if(r){
44884                 sels.push(this.store.indexOf(r))
44885                 
44886             }
44887         },this);
44888         this.view.select(sels);
44889         return false;
44890     },
44891     
44892     
44893     
44894     onSelect : function(record, index){
44895        // Roo.log("onselect Called");
44896        // this is only called by the clear button now..
44897         this.view.clearSelections();
44898         this.setValue('[]');
44899         if (this.value != this.valueBefore) {
44900             this.fireEvent('change', this, this.value, this.valueBefore);
44901         }
44902     },
44903     getValueArray : function()
44904     {
44905         var ar = [] ;
44906         
44907         try {
44908             //Roo.log(this.value);
44909             if (typeof(this.value) == 'undefined') {
44910                 return [];
44911             }
44912             var ar = Roo.decode(this.value);
44913             return  ar instanceof Array ? ar : []; //?? valid?
44914             
44915         } catch(e) {
44916             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
44917             return [];
44918         }
44919          
44920     },
44921     expand : function ()
44922     {
44923         Roo.form.ComboCheck.superclass.expand.call(this);
44924         this.valueBefore = this.value;
44925         
44926
44927     },
44928     
44929     collapse : function(){
44930         Roo.form.ComboCheck.superclass.collapse.call(this);
44931         var sl = this.view.getSelectedIndexes();
44932         var st = this.store;
44933         var nv = [];
44934         var tv = [];
44935         var r;
44936         Roo.each(sl, function(i) {
44937             r = st.getAt(i);
44938             nv.push(r.get(this.valueField));
44939         },this);
44940         this.setValue(Roo.encode(nv));
44941         if (this.value != this.valueBefore) {
44942
44943             this.fireEvent('change', this, this.value, this.valueBefore);
44944         }
44945         
44946     },
44947     
44948     setValue : function(v){
44949         // Roo.log(v);
44950         this.value = v;
44951         
44952         var vals = this.getValueArray();
44953         var tv = [];
44954         Roo.each(vals, function(k) {
44955             var r = this.findRecord(this.valueField, k);
44956             if(r){
44957                 tv.push(r.data[this.displayField]);
44958             }else if(this.valueNotFoundText !== undefined){
44959                 tv.push( this.valueNotFoundText );
44960             }
44961         },this);
44962        // Roo.log(tv);
44963         
44964         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
44965         this.hiddenField.value = v;
44966         this.value = v;
44967     }
44968     
44969 });//<script type="text/javasscript">
44970  
44971
44972 /**
44973  * @class Roo.DDView
44974  * A DnD enabled version of Roo.View.
44975  * @param {Element/String} container The Element in which to create the View.
44976  * @param {String} tpl The template string used to create the markup for each element of the View
44977  * @param {Object} config The configuration properties. These include all the config options of
44978  * {@link Roo.View} plus some specific to this class.<br>
44979  * <p>
44980  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
44981  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
44982  * <p>
44983  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
44984 .x-view-drag-insert-above {
44985         border-top:1px dotted #3366cc;
44986 }
44987 .x-view-drag-insert-below {
44988         border-bottom:1px dotted #3366cc;
44989 }
44990 </code></pre>
44991  * 
44992  */
44993  
44994 Roo.DDView = function(container, tpl, config) {
44995     Roo.DDView.superclass.constructor.apply(this, arguments);
44996     this.getEl().setStyle("outline", "0px none");
44997     this.getEl().unselectable();
44998     if (this.dragGroup) {
44999                 this.setDraggable(this.dragGroup.split(","));
45000     }
45001     if (this.dropGroup) {
45002                 this.setDroppable(this.dropGroup.split(","));
45003     }
45004     if (this.deletable) {
45005         this.setDeletable();
45006     }
45007     this.isDirtyFlag = false;
45008         this.addEvents({
45009                 "drop" : true
45010         });
45011 };
45012
45013 Roo.extend(Roo.DDView, Roo.View, {
45014 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
45015 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
45016 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
45017 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
45018
45019         isFormField: true,
45020
45021         reset: Roo.emptyFn,
45022         
45023         clearInvalid: Roo.form.Field.prototype.clearInvalid,
45024
45025         validate: function() {
45026                 return true;
45027         },
45028         
45029         destroy: function() {
45030                 this.purgeListeners();
45031                 this.getEl.removeAllListeners();
45032                 this.getEl().remove();
45033                 if (this.dragZone) {
45034                         if (this.dragZone.destroy) {
45035                                 this.dragZone.destroy();
45036                         }
45037                 }
45038                 if (this.dropZone) {
45039                         if (this.dropZone.destroy) {
45040                                 this.dropZone.destroy();
45041                         }
45042                 }
45043         },
45044
45045 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
45046         getName: function() {
45047                 return this.name;
45048         },
45049
45050 /**     Loads the View from a JSON string representing the Records to put into the Store. */
45051         setValue: function(v) {
45052                 if (!this.store) {
45053                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
45054                 }
45055                 var data = {};
45056                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
45057                 this.store.proxy = new Roo.data.MemoryProxy(data);
45058                 this.store.load();
45059         },
45060
45061 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
45062         getValue: function() {
45063                 var result = '(';
45064                 this.store.each(function(rec) {
45065                         result += rec.id + ',';
45066                 });
45067                 return result.substr(0, result.length - 1) + ')';
45068         },
45069         
45070         getIds: function() {
45071                 var i = 0, result = new Array(this.store.getCount());
45072                 this.store.each(function(rec) {
45073                         result[i++] = rec.id;
45074                 });
45075                 return result;
45076         },
45077         
45078         isDirty: function() {
45079                 return this.isDirtyFlag;
45080         },
45081
45082 /**
45083  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
45084  *      whole Element becomes the target, and this causes the drop gesture to append.
45085  */
45086     getTargetFromEvent : function(e) {
45087                 var target = e.getTarget();
45088                 while ((target !== null) && (target.parentNode != this.el.dom)) {
45089                 target = target.parentNode;
45090                 }
45091                 if (!target) {
45092                         target = this.el.dom.lastChild || this.el.dom;
45093                 }
45094                 return target;
45095     },
45096
45097 /**
45098  *      Create the drag data which consists of an object which has the property "ddel" as
45099  *      the drag proxy element. 
45100  */
45101     getDragData : function(e) {
45102         var target = this.findItemFromChild(e.getTarget());
45103                 if(target) {
45104                         this.handleSelection(e);
45105                         var selNodes = this.getSelectedNodes();
45106             var dragData = {
45107                 source: this,
45108                 copy: this.copy || (this.allowCopy && e.ctrlKey),
45109                 nodes: selNodes,
45110                 records: []
45111                         };
45112                         var selectedIndices = this.getSelectedIndexes();
45113                         for (var i = 0; i < selectedIndices.length; i++) {
45114                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
45115                         }
45116                         if (selNodes.length == 1) {
45117                                 dragData.ddel = target.cloneNode(true); // the div element
45118                         } else {
45119                                 var div = document.createElement('div'); // create the multi element drag "ghost"
45120                                 div.className = 'multi-proxy';
45121                                 for (var i = 0, len = selNodes.length; i < len; i++) {
45122                                         div.appendChild(selNodes[i].cloneNode(true));
45123                                 }
45124                                 dragData.ddel = div;
45125                         }
45126             //console.log(dragData)
45127             //console.log(dragData.ddel.innerHTML)
45128                         return dragData;
45129                 }
45130         //console.log('nodragData')
45131                 return false;
45132     },
45133     
45134 /**     Specify to which ddGroup items in this DDView may be dragged. */
45135     setDraggable: function(ddGroup) {
45136         if (ddGroup instanceof Array) {
45137                 Roo.each(ddGroup, this.setDraggable, this);
45138                 return;
45139         }
45140         if (this.dragZone) {
45141                 this.dragZone.addToGroup(ddGroup);
45142         } else {
45143                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
45144                                 containerScroll: true,
45145                                 ddGroup: ddGroup 
45146
45147                         });
45148 //                      Draggability implies selection. DragZone's mousedown selects the element.
45149                         if (!this.multiSelect) { this.singleSelect = true; }
45150
45151 //                      Wire the DragZone's handlers up to methods in *this*
45152                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
45153                 }
45154     },
45155
45156 /**     Specify from which ddGroup this DDView accepts drops. */
45157     setDroppable: function(ddGroup) {
45158         if (ddGroup instanceof Array) {
45159                 Roo.each(ddGroup, this.setDroppable, this);
45160                 return;
45161         }
45162         if (this.dropZone) {
45163                 this.dropZone.addToGroup(ddGroup);
45164         } else {
45165                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
45166                                 containerScroll: true,
45167                                 ddGroup: ddGroup
45168                         });
45169
45170 //                      Wire the DropZone's handlers up to methods in *this*
45171                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
45172                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
45173                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
45174                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
45175                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
45176                 }
45177     },
45178
45179 /**     Decide whether to drop above or below a View node. */
45180     getDropPoint : function(e, n, dd){
45181         if (n == this.el.dom) { return "above"; }
45182                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
45183                 var c = t + (b - t) / 2;
45184                 var y = Roo.lib.Event.getPageY(e);
45185                 if(y <= c) {
45186                         return "above";
45187                 }else{
45188                         return "below";
45189                 }
45190     },
45191
45192     onNodeEnter : function(n, dd, e, data){
45193                 return false;
45194     },
45195     
45196     onNodeOver : function(n, dd, e, data){
45197                 var pt = this.getDropPoint(e, n, dd);
45198                 // set the insert point style on the target node
45199                 var dragElClass = this.dropNotAllowed;
45200                 if (pt) {
45201                         var targetElClass;
45202                         if (pt == "above"){
45203                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
45204                                 targetElClass = "x-view-drag-insert-above";
45205                         } else {
45206                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
45207                                 targetElClass = "x-view-drag-insert-below";
45208                         }
45209                         if (this.lastInsertClass != targetElClass){
45210                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
45211                                 this.lastInsertClass = targetElClass;
45212                         }
45213                 }
45214                 return dragElClass;
45215         },
45216
45217     onNodeOut : function(n, dd, e, data){
45218                 this.removeDropIndicators(n);
45219     },
45220
45221     onNodeDrop : function(n, dd, e, data){
45222         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
45223                 return false;
45224         }
45225         var pt = this.getDropPoint(e, n, dd);
45226                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
45227                 if (pt == "below") { insertAt++; }
45228                 for (var i = 0; i < data.records.length; i++) {
45229                         var r = data.records[i];
45230                         var dup = this.store.getById(r.id);
45231                         if (dup && (dd != this.dragZone)) {
45232                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
45233                         } else {
45234                                 if (data.copy) {
45235                                         this.store.insert(insertAt++, r.copy());
45236                                 } else {
45237                                         data.source.isDirtyFlag = true;
45238                                         r.store.remove(r);
45239                                         this.store.insert(insertAt++, r);
45240                                 }
45241                                 this.isDirtyFlag = true;
45242                         }
45243                 }
45244                 this.dragZone.cachedTarget = null;
45245                 return true;
45246     },
45247
45248     removeDropIndicators : function(n){
45249                 if(n){
45250                         Roo.fly(n).removeClass([
45251                                 "x-view-drag-insert-above",
45252                                 "x-view-drag-insert-below"]);
45253                         this.lastInsertClass = "_noclass";
45254                 }
45255     },
45256
45257 /**
45258  *      Utility method. Add a delete option to the DDView's context menu.
45259  *      @param {String} imageUrl The URL of the "delete" icon image.
45260  */
45261         setDeletable: function(imageUrl) {
45262                 if (!this.singleSelect && !this.multiSelect) {
45263                         this.singleSelect = true;
45264                 }
45265                 var c = this.getContextMenu();
45266                 this.contextMenu.on("itemclick", function(item) {
45267                         switch (item.id) {
45268                                 case "delete":
45269                                         this.remove(this.getSelectedIndexes());
45270                                         break;
45271                         }
45272                 }, this);
45273                 this.contextMenu.add({
45274                         icon: imageUrl,
45275                         id: "delete",
45276                         text: 'Delete'
45277                 });
45278         },
45279         
45280 /**     Return the context menu for this DDView. */
45281         getContextMenu: function() {
45282                 if (!this.contextMenu) {
45283 //                      Create the View's context menu
45284                         this.contextMenu = new Roo.menu.Menu({
45285                                 id: this.id + "-contextmenu"
45286                         });
45287                         this.el.on("contextmenu", this.showContextMenu, this);
45288                 }
45289                 return this.contextMenu;
45290         },
45291         
45292         disableContextMenu: function() {
45293                 if (this.contextMenu) {
45294                         this.el.un("contextmenu", this.showContextMenu, this);
45295                 }
45296         },
45297
45298         showContextMenu: function(e, item) {
45299         item = this.findItemFromChild(e.getTarget());
45300                 if (item) {
45301                         e.stopEvent();
45302                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
45303                         this.contextMenu.showAt(e.getXY());
45304             }
45305     },
45306
45307 /**
45308  *      Remove {@link Roo.data.Record}s at the specified indices.
45309  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
45310  */
45311     remove: function(selectedIndices) {
45312                 selectedIndices = [].concat(selectedIndices);
45313                 for (var i = 0; i < selectedIndices.length; i++) {
45314                         var rec = this.store.getAt(selectedIndices[i]);
45315                         this.store.remove(rec);
45316                 }
45317     },
45318
45319 /**
45320  *      Double click fires the event, but also, if this is draggable, and there is only one other
45321  *      related DropZone, it transfers the selected node.
45322  */
45323     onDblClick : function(e){
45324         var item = this.findItemFromChild(e.getTarget());
45325         if(item){
45326             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
45327                 return false;
45328             }
45329             if (this.dragGroup) {
45330                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
45331                     while (targets.indexOf(this.dropZone) > -1) {
45332                             targets.remove(this.dropZone);
45333                                 }
45334                     if (targets.length == 1) {
45335                                         this.dragZone.cachedTarget = null;
45336                         var el = Roo.get(targets[0].getEl());
45337                         var box = el.getBox(true);
45338                         targets[0].onNodeDrop(el.dom, {
45339                                 target: el.dom,
45340                                 xy: [box.x, box.y + box.height - 1]
45341                         }, null, this.getDragData(e));
45342                     }
45343                 }
45344         }
45345     },
45346     
45347     handleSelection: function(e) {
45348                 this.dragZone.cachedTarget = null;
45349         var item = this.findItemFromChild(e.getTarget());
45350         if (!item) {
45351                 this.clearSelections(true);
45352                 return;
45353         }
45354                 if (item && (this.multiSelect || this.singleSelect)){
45355                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
45356                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
45357                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
45358                                 this.unselect(item);
45359                         } else {
45360                                 this.select(item, this.multiSelect && e.ctrlKey);
45361                                 this.lastSelection = item;
45362                         }
45363                 }
45364     },
45365
45366     onItemClick : function(item, index, e){
45367                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
45368                         return false;
45369                 }
45370                 return true;
45371     },
45372
45373     unselect : function(nodeInfo, suppressEvent){
45374                 var node = this.getNode(nodeInfo);
45375                 if(node && this.isSelected(node)){
45376                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
45377                                 Roo.fly(node).removeClass(this.selectedClass);
45378                                 this.selections.remove(node);
45379                                 if(!suppressEvent){
45380                                         this.fireEvent("selectionchange", this, this.selections);
45381                                 }
45382                         }
45383                 }
45384     }
45385 });
45386 /*
45387  * Based on:
45388  * Ext JS Library 1.1.1
45389  * Copyright(c) 2006-2007, Ext JS, LLC.
45390  *
45391  * Originally Released Under LGPL - original licence link has changed is not relivant.
45392  *
45393  * Fork - LGPL
45394  * <script type="text/javascript">
45395  */
45396  
45397 /**
45398  * @class Roo.LayoutManager
45399  * @extends Roo.util.Observable
45400  * Base class for layout managers.
45401  */
45402 Roo.LayoutManager = function(container, config){
45403     Roo.LayoutManager.superclass.constructor.call(this);
45404     this.el = Roo.get(container);
45405     // ie scrollbar fix
45406     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
45407         document.body.scroll = "no";
45408     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
45409         this.el.position('relative');
45410     }
45411     this.id = this.el.id;
45412     this.el.addClass("x-layout-container");
45413     /** false to disable window resize monitoring @type Boolean */
45414     this.monitorWindowResize = true;
45415     this.regions = {};
45416     this.addEvents({
45417         /**
45418          * @event layout
45419          * Fires when a layout is performed. 
45420          * @param {Roo.LayoutManager} this
45421          */
45422         "layout" : true,
45423         /**
45424          * @event regionresized
45425          * Fires when the user resizes a region. 
45426          * @param {Roo.LayoutRegion} region The resized region
45427          * @param {Number} newSize The new size (width for east/west, height for north/south)
45428          */
45429         "regionresized" : true,
45430         /**
45431          * @event regioncollapsed
45432          * Fires when a region is collapsed. 
45433          * @param {Roo.LayoutRegion} region The collapsed region
45434          */
45435         "regioncollapsed" : true,
45436         /**
45437          * @event regionexpanded
45438          * Fires when a region is expanded.  
45439          * @param {Roo.LayoutRegion} region The expanded region
45440          */
45441         "regionexpanded" : true
45442     });
45443     this.updating = false;
45444     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
45445 };
45446
45447 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
45448     /**
45449      * Returns true if this layout is currently being updated
45450      * @return {Boolean}
45451      */
45452     isUpdating : function(){
45453         return this.updating; 
45454     },
45455     
45456     /**
45457      * Suspend the LayoutManager from doing auto-layouts while
45458      * making multiple add or remove calls
45459      */
45460     beginUpdate : function(){
45461         this.updating = true;    
45462     },
45463     
45464     /**
45465      * Restore auto-layouts and optionally disable the manager from performing a layout
45466      * @param {Boolean} noLayout true to disable a layout update 
45467      */
45468     endUpdate : function(noLayout){
45469         this.updating = false;
45470         if(!noLayout){
45471             this.layout();
45472         }    
45473     },
45474     
45475     layout: function(){
45476         
45477     },
45478     
45479     onRegionResized : function(region, newSize){
45480         this.fireEvent("regionresized", region, newSize);
45481         this.layout();
45482     },
45483     
45484     onRegionCollapsed : function(region){
45485         this.fireEvent("regioncollapsed", region);
45486     },
45487     
45488     onRegionExpanded : function(region){
45489         this.fireEvent("regionexpanded", region);
45490     },
45491         
45492     /**
45493      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
45494      * performs box-model adjustments.
45495      * @return {Object} The size as an object {width: (the width), height: (the height)}
45496      */
45497     getViewSize : function(){
45498         var size;
45499         if(this.el.dom != document.body){
45500             size = this.el.getSize();
45501         }else{
45502             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
45503         }
45504         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
45505         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45506         return size;
45507     },
45508     
45509     /**
45510      * Returns the Element this layout is bound to.
45511      * @return {Roo.Element}
45512      */
45513     getEl : function(){
45514         return this.el;
45515     },
45516     
45517     /**
45518      * Returns the specified region.
45519      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
45520      * @return {Roo.LayoutRegion}
45521      */
45522     getRegion : function(target){
45523         return this.regions[target.toLowerCase()];
45524     },
45525     
45526     onWindowResize : function(){
45527         if(this.monitorWindowResize){
45528             this.layout();
45529         }
45530     }
45531 });/*
45532  * Based on:
45533  * Ext JS Library 1.1.1
45534  * Copyright(c) 2006-2007, Ext JS, LLC.
45535  *
45536  * Originally Released Under LGPL - original licence link has changed is not relivant.
45537  *
45538  * Fork - LGPL
45539  * <script type="text/javascript">
45540  */
45541 /**
45542  * @class Roo.BorderLayout
45543  * @extends Roo.LayoutManager
45544  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
45545  * please see: <br><br>
45546  * <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>
45547  * <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>
45548  * Example:
45549  <pre><code>
45550  var layout = new Roo.BorderLayout(document.body, {
45551     north: {
45552         initialSize: 25,
45553         titlebar: false
45554     },
45555     west: {
45556         split:true,
45557         initialSize: 200,
45558         minSize: 175,
45559         maxSize: 400,
45560         titlebar: true,
45561         collapsible: true
45562     },
45563     east: {
45564         split:true,
45565         initialSize: 202,
45566         minSize: 175,
45567         maxSize: 400,
45568         titlebar: true,
45569         collapsible: true
45570     },
45571     south: {
45572         split:true,
45573         initialSize: 100,
45574         minSize: 100,
45575         maxSize: 200,
45576         titlebar: true,
45577         collapsible: true
45578     },
45579     center: {
45580         titlebar: true,
45581         autoScroll:true,
45582         resizeTabs: true,
45583         minTabWidth: 50,
45584         preferredTabWidth: 150
45585     }
45586 });
45587
45588 // shorthand
45589 var CP = Roo.ContentPanel;
45590
45591 layout.beginUpdate();
45592 layout.add("north", new CP("north", "North"));
45593 layout.add("south", new CP("south", {title: "South", closable: true}));
45594 layout.add("west", new CP("west", {title: "West"}));
45595 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
45596 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
45597 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
45598 layout.getRegion("center").showPanel("center1");
45599 layout.endUpdate();
45600 </code></pre>
45601
45602 <b>The container the layout is rendered into can be either the body element or any other element.
45603 If it is not the body element, the container needs to either be an absolute positioned element,
45604 or you will need to add "position:relative" to the css of the container.  You will also need to specify
45605 the container size if it is not the body element.</b>
45606
45607 * @constructor
45608 * Create a new BorderLayout
45609 * @param {String/HTMLElement/Element} container The container this layout is bound to
45610 * @param {Object} config Configuration options
45611  */
45612 Roo.BorderLayout = function(container, config){
45613     config = config || {};
45614     Roo.BorderLayout.superclass.constructor.call(this, container, config);
45615     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
45616     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
45617         var target = this.factory.validRegions[i];
45618         if(config[target]){
45619             this.addRegion(target, config[target]);
45620         }
45621     }
45622 };
45623
45624 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
45625     /**
45626      * Creates and adds a new region if it doesn't already exist.
45627      * @param {String} target The target region key (north, south, east, west or center).
45628      * @param {Object} config The regions config object
45629      * @return {BorderLayoutRegion} The new region
45630      */
45631     addRegion : function(target, config){
45632         if(!this.regions[target]){
45633             var r = this.factory.create(target, this, config);
45634             this.bindRegion(target, r);
45635         }
45636         return this.regions[target];
45637     },
45638
45639     // private (kinda)
45640     bindRegion : function(name, r){
45641         this.regions[name] = r;
45642         r.on("visibilitychange", this.layout, this);
45643         r.on("paneladded", this.layout, this);
45644         r.on("panelremoved", this.layout, this);
45645         r.on("invalidated", this.layout, this);
45646         r.on("resized", this.onRegionResized, this);
45647         r.on("collapsed", this.onRegionCollapsed, this);
45648         r.on("expanded", this.onRegionExpanded, this);
45649     },
45650
45651     /**
45652      * Performs a layout update.
45653      */
45654     layout : function(){
45655         if(this.updating) return;
45656         var size = this.getViewSize();
45657         var w = size.width;
45658         var h = size.height;
45659         var centerW = w;
45660         var centerH = h;
45661         var centerY = 0;
45662         var centerX = 0;
45663         //var x = 0, y = 0;
45664
45665         var rs = this.regions;
45666         var north = rs["north"];
45667         var south = rs["south"]; 
45668         var west = rs["west"];
45669         var east = rs["east"];
45670         var center = rs["center"];
45671         //if(this.hideOnLayout){ // not supported anymore
45672             //c.el.setStyle("display", "none");
45673         //}
45674         if(north && north.isVisible()){
45675             var b = north.getBox();
45676             var m = north.getMargins();
45677             b.width = w - (m.left+m.right);
45678             b.x = m.left;
45679             b.y = m.top;
45680             centerY = b.height + b.y + m.bottom;
45681             centerH -= centerY;
45682             north.updateBox(this.safeBox(b));
45683         }
45684         if(south && south.isVisible()){
45685             var b = south.getBox();
45686             var m = south.getMargins();
45687             b.width = w - (m.left+m.right);
45688             b.x = m.left;
45689             var totalHeight = (b.height + m.top + m.bottom);
45690             b.y = h - totalHeight + m.top;
45691             centerH -= totalHeight;
45692             south.updateBox(this.safeBox(b));
45693         }
45694         if(west && west.isVisible()){
45695             var b = west.getBox();
45696             var m = west.getMargins();
45697             b.height = centerH - (m.top+m.bottom);
45698             b.x = m.left;
45699             b.y = centerY + m.top;
45700             var totalWidth = (b.width + m.left + m.right);
45701             centerX += totalWidth;
45702             centerW -= totalWidth;
45703             west.updateBox(this.safeBox(b));
45704         }
45705         if(east && east.isVisible()){
45706             var b = east.getBox();
45707             var m = east.getMargins();
45708             b.height = centerH - (m.top+m.bottom);
45709             var totalWidth = (b.width + m.left + m.right);
45710             b.x = w - totalWidth + m.left;
45711             b.y = centerY + m.top;
45712             centerW -= totalWidth;
45713             east.updateBox(this.safeBox(b));
45714         }
45715         if(center){
45716             var m = center.getMargins();
45717             var centerBox = {
45718                 x: centerX + m.left,
45719                 y: centerY + m.top,
45720                 width: centerW - (m.left+m.right),
45721                 height: centerH - (m.top+m.bottom)
45722             };
45723             //if(this.hideOnLayout){
45724                 //center.el.setStyle("display", "block");
45725             //}
45726             center.updateBox(this.safeBox(centerBox));
45727         }
45728         this.el.repaint();
45729         this.fireEvent("layout", this);
45730     },
45731
45732     // private
45733     safeBox : function(box){
45734         box.width = Math.max(0, box.width);
45735         box.height = Math.max(0, box.height);
45736         return box;
45737     },
45738
45739     /**
45740      * Adds a ContentPanel (or subclass) to this layout.
45741      * @param {String} target The target region key (north, south, east, west or center).
45742      * @param {Roo.ContentPanel} panel The panel to add
45743      * @return {Roo.ContentPanel} The added panel
45744      */
45745     add : function(target, panel){
45746          
45747         target = target.toLowerCase();
45748         return this.regions[target].add(panel);
45749     },
45750
45751     /**
45752      * Remove a ContentPanel (or subclass) to this layout.
45753      * @param {String} target The target region key (north, south, east, west or center).
45754      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
45755      * @return {Roo.ContentPanel} The removed panel
45756      */
45757     remove : function(target, panel){
45758         target = target.toLowerCase();
45759         return this.regions[target].remove(panel);
45760     },
45761
45762     /**
45763      * Searches all regions for a panel with the specified id
45764      * @param {String} panelId
45765      * @return {Roo.ContentPanel} The panel or null if it wasn't found
45766      */
45767     findPanel : function(panelId){
45768         var rs = this.regions;
45769         for(var target in rs){
45770             if(typeof rs[target] != "function"){
45771                 var p = rs[target].getPanel(panelId);
45772                 if(p){
45773                     return p;
45774                 }
45775             }
45776         }
45777         return null;
45778     },
45779
45780     /**
45781      * Searches all regions for a panel with the specified id and activates (shows) it.
45782      * @param {String/ContentPanel} panelId The panels id or the panel itself
45783      * @return {Roo.ContentPanel} The shown panel or null
45784      */
45785     showPanel : function(panelId) {
45786       var rs = this.regions;
45787       for(var target in rs){
45788          var r = rs[target];
45789          if(typeof r != "function"){
45790             if(r.hasPanel(panelId)){
45791                return r.showPanel(panelId);
45792             }
45793          }
45794       }
45795       return null;
45796    },
45797
45798    /**
45799      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
45800      * @param {Roo.state.Provider} provider (optional) An alternate state provider
45801      */
45802     restoreState : function(provider){
45803         if(!provider){
45804             provider = Roo.state.Manager;
45805         }
45806         var sm = new Roo.LayoutStateManager();
45807         sm.init(this, provider);
45808     },
45809
45810     /**
45811      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
45812      * object should contain properties for each region to add ContentPanels to, and each property's value should be
45813      * a valid ContentPanel config object.  Example:
45814      * <pre><code>
45815 // Create the main layout
45816 var layout = new Roo.BorderLayout('main-ct', {
45817     west: {
45818         split:true,
45819         minSize: 175,
45820         titlebar: true
45821     },
45822     center: {
45823         title:'Components'
45824     }
45825 }, 'main-ct');
45826
45827 // Create and add multiple ContentPanels at once via configs
45828 layout.batchAdd({
45829    west: {
45830        id: 'source-files',
45831        autoCreate:true,
45832        title:'Ext Source Files',
45833        autoScroll:true,
45834        fitToFrame:true
45835    },
45836    center : {
45837        el: cview,
45838        autoScroll:true,
45839        fitToFrame:true,
45840        toolbar: tb,
45841        resizeEl:'cbody'
45842    }
45843 });
45844 </code></pre>
45845      * @param {Object} regions An object containing ContentPanel configs by region name
45846      */
45847     batchAdd : function(regions){
45848         this.beginUpdate();
45849         for(var rname in regions){
45850             var lr = this.regions[rname];
45851             if(lr){
45852                 this.addTypedPanels(lr, regions[rname]);
45853             }
45854         }
45855         this.endUpdate();
45856     },
45857
45858     // private
45859     addTypedPanels : function(lr, ps){
45860         if(typeof ps == 'string'){
45861             lr.add(new Roo.ContentPanel(ps));
45862         }
45863         else if(ps instanceof Array){
45864             for(var i =0, len = ps.length; i < len; i++){
45865                 this.addTypedPanels(lr, ps[i]);
45866             }
45867         }
45868         else if(!ps.events){ // raw config?
45869             var el = ps.el;
45870             delete ps.el; // prevent conflict
45871             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
45872         }
45873         else {  // panel object assumed!
45874             lr.add(ps);
45875         }
45876     },
45877     /**
45878      * Adds a xtype elements to the layout.
45879      * <pre><code>
45880
45881 layout.addxtype({
45882        xtype : 'ContentPanel',
45883        region: 'west',
45884        items: [ .... ]
45885    }
45886 );
45887
45888 layout.addxtype({
45889         xtype : 'NestedLayoutPanel',
45890         region: 'west',
45891         layout: {
45892            center: { },
45893            west: { }   
45894         },
45895         items : [ ... list of content panels or nested layout panels.. ]
45896    }
45897 );
45898 </code></pre>
45899      * @param {Object} cfg Xtype definition of item to add.
45900      */
45901     addxtype : function(cfg)
45902     {
45903         // basically accepts a pannel...
45904         // can accept a layout region..!?!?
45905         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
45906         
45907         if (!cfg.xtype.match(/Panel$/)) {
45908             return false;
45909         }
45910         var ret = false;
45911         
45912         if (typeof(cfg.region) == 'undefined') {
45913             Roo.log("Failed to add Panel, region was not set");
45914             Roo.log(cfg);
45915             return false;
45916         }
45917         var region = cfg.region;
45918         delete cfg.region;
45919         
45920           
45921         var xitems = [];
45922         if (cfg.items) {
45923             xitems = cfg.items;
45924             delete cfg.items;
45925         }
45926         var nb = false;
45927         
45928         switch(cfg.xtype) 
45929         {
45930             case 'ContentPanel':  // ContentPanel (el, cfg)
45931             case 'ScrollPanel':  // ContentPanel (el, cfg)
45932                 if(cfg.autoCreate) {
45933                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
45934                 } else {
45935                     var el = this.el.createChild();
45936                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
45937                 }
45938                 
45939                 this.add(region, ret);
45940                 break;
45941             
45942             
45943             case 'TreePanel': // our new panel!
45944                 cfg.el = this.el.createChild();
45945                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
45946                 this.add(region, ret);
45947                 break;
45948             
45949             case 'NestedLayoutPanel': 
45950                 // create a new Layout (which is  a Border Layout...
45951                 var el = this.el.createChild();
45952                 var clayout = cfg.layout;
45953                 delete cfg.layout;
45954                 clayout.items   = clayout.items  || [];
45955                 // replace this exitems with the clayout ones..
45956                 xitems = clayout.items;
45957                  
45958                 
45959                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
45960                     cfg.background = false;
45961                 }
45962                 var layout = new Roo.BorderLayout(el, clayout);
45963                 
45964                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
45965                 //console.log('adding nested layout panel '  + cfg.toSource());
45966                 this.add(region, ret);
45967                 nb = {}; /// find first...
45968                 break;
45969                 
45970             case 'GridPanel': 
45971             
45972                 // needs grid and region
45973                 
45974                 //var el = this.getRegion(region).el.createChild();
45975                 var el = this.el.createChild();
45976                 // create the grid first...
45977                 
45978                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
45979                 delete cfg.grid;
45980                 if (region == 'center' && this.active ) {
45981                     cfg.background = false;
45982                 }
45983                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
45984                 
45985                 this.add(region, ret);
45986                 if (cfg.background) {
45987                     ret.on('activate', function(gp) {
45988                         if (!gp.grid.rendered) {
45989                             gp.grid.render();
45990                         }
45991                     });
45992                 } else {
45993                     grid.render();
45994                 }
45995                 break;
45996            
45997                
45998                 
45999                 
46000             default: 
46001                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
46002                 return null;
46003              // GridPanel (grid, cfg)
46004             
46005         }
46006         this.beginUpdate();
46007         // add children..
46008         var region = '';
46009         var abn = {};
46010         Roo.each(xitems, function(i)  {
46011             region = nb && i.region ? i.region : false;
46012             
46013             var add = ret.addxtype(i);
46014            
46015             if (region) {
46016                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
46017                 if (!i.background) {
46018                     abn[region] = nb[region] ;
46019                 }
46020             }
46021             
46022         });
46023         this.endUpdate();
46024
46025         // make the last non-background panel active..
46026         //if (nb) { Roo.log(abn); }
46027         if (nb) {
46028             
46029             for(var r in abn) {
46030                 region = this.getRegion(r);
46031                 if (region) {
46032                     // tried using nb[r], but it does not work..
46033                      
46034                     region.showPanel(abn[r]);
46035                    
46036                 }
46037             }
46038         }
46039         return ret;
46040         
46041     }
46042 });
46043
46044 /**
46045  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
46046  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
46047  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
46048  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
46049  * <pre><code>
46050 // shorthand
46051 var CP = Roo.ContentPanel;
46052
46053 var layout = Roo.BorderLayout.create({
46054     north: {
46055         initialSize: 25,
46056         titlebar: false,
46057         panels: [new CP("north", "North")]
46058     },
46059     west: {
46060         split:true,
46061         initialSize: 200,
46062         minSize: 175,
46063         maxSize: 400,
46064         titlebar: true,
46065         collapsible: true,
46066         panels: [new CP("west", {title: "West"})]
46067     },
46068     east: {
46069         split:true,
46070         initialSize: 202,
46071         minSize: 175,
46072         maxSize: 400,
46073         titlebar: true,
46074         collapsible: true,
46075         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
46076     },
46077     south: {
46078         split:true,
46079         initialSize: 100,
46080         minSize: 100,
46081         maxSize: 200,
46082         titlebar: true,
46083         collapsible: true,
46084         panels: [new CP("south", {title: "South", closable: true})]
46085     },
46086     center: {
46087         titlebar: true,
46088         autoScroll:true,
46089         resizeTabs: true,
46090         minTabWidth: 50,
46091         preferredTabWidth: 150,
46092         panels: [
46093             new CP("center1", {title: "Close Me", closable: true}),
46094             new CP("center2", {title: "Center Panel", closable: false})
46095         ]
46096     }
46097 }, document.body);
46098
46099 layout.getRegion("center").showPanel("center1");
46100 </code></pre>
46101  * @param config
46102  * @param targetEl
46103  */
46104 Roo.BorderLayout.create = function(config, targetEl){
46105     var layout = new Roo.BorderLayout(targetEl || document.body, config);
46106     layout.beginUpdate();
46107     var regions = Roo.BorderLayout.RegionFactory.validRegions;
46108     for(var j = 0, jlen = regions.length; j < jlen; j++){
46109         var lr = regions[j];
46110         if(layout.regions[lr] && config[lr].panels){
46111             var r = layout.regions[lr];
46112             var ps = config[lr].panels;
46113             layout.addTypedPanels(r, ps);
46114         }
46115     }
46116     layout.endUpdate();
46117     return layout;
46118 };
46119
46120 // private
46121 Roo.BorderLayout.RegionFactory = {
46122     // private
46123     validRegions : ["north","south","east","west","center"],
46124
46125     // private
46126     create : function(target, mgr, config){
46127         target = target.toLowerCase();
46128         if(config.lightweight || config.basic){
46129             return new Roo.BasicLayoutRegion(mgr, config, target);
46130         }
46131         switch(target){
46132             case "north":
46133                 return new Roo.NorthLayoutRegion(mgr, config);
46134             case "south":
46135                 return new Roo.SouthLayoutRegion(mgr, config);
46136             case "east":
46137                 return new Roo.EastLayoutRegion(mgr, config);
46138             case "west":
46139                 return new Roo.WestLayoutRegion(mgr, config);
46140             case "center":
46141                 return new Roo.CenterLayoutRegion(mgr, config);
46142         }
46143         throw 'Layout region "'+target+'" not supported.';
46144     }
46145 };/*
46146  * Based on:
46147  * Ext JS Library 1.1.1
46148  * Copyright(c) 2006-2007, Ext JS, LLC.
46149  *
46150  * Originally Released Under LGPL - original licence link has changed is not relivant.
46151  *
46152  * Fork - LGPL
46153  * <script type="text/javascript">
46154  */
46155  
46156 /**
46157  * @class Roo.BasicLayoutRegion
46158  * @extends Roo.util.Observable
46159  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
46160  * and does not have a titlebar, tabs or any other features. All it does is size and position 
46161  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
46162  */
46163 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
46164     this.mgr = mgr;
46165     this.position  = pos;
46166     this.events = {
46167         /**
46168          * @scope Roo.BasicLayoutRegion
46169          */
46170         
46171         /**
46172          * @event beforeremove
46173          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
46174          * @param {Roo.LayoutRegion} this
46175          * @param {Roo.ContentPanel} panel The panel
46176          * @param {Object} e The cancel event object
46177          */
46178         "beforeremove" : true,
46179         /**
46180          * @event invalidated
46181          * Fires when the layout for this region is changed.
46182          * @param {Roo.LayoutRegion} this
46183          */
46184         "invalidated" : true,
46185         /**
46186          * @event visibilitychange
46187          * Fires when this region is shown or hidden 
46188          * @param {Roo.LayoutRegion} this
46189          * @param {Boolean} visibility true or false
46190          */
46191         "visibilitychange" : true,
46192         /**
46193          * @event paneladded
46194          * Fires when a panel is added. 
46195          * @param {Roo.LayoutRegion} this
46196          * @param {Roo.ContentPanel} panel The panel
46197          */
46198         "paneladded" : true,
46199         /**
46200          * @event panelremoved
46201          * Fires when a panel is removed. 
46202          * @param {Roo.LayoutRegion} this
46203          * @param {Roo.ContentPanel} panel The panel
46204          */
46205         "panelremoved" : true,
46206         /**
46207          * @event collapsed
46208          * Fires when this region is collapsed.
46209          * @param {Roo.LayoutRegion} this
46210          */
46211         "collapsed" : true,
46212         /**
46213          * @event expanded
46214          * Fires when this region is expanded.
46215          * @param {Roo.LayoutRegion} this
46216          */
46217         "expanded" : true,
46218         /**
46219          * @event slideshow
46220          * Fires when this region is slid into view.
46221          * @param {Roo.LayoutRegion} this
46222          */
46223         "slideshow" : true,
46224         /**
46225          * @event slidehide
46226          * Fires when this region slides out of view. 
46227          * @param {Roo.LayoutRegion} this
46228          */
46229         "slidehide" : true,
46230         /**
46231          * @event panelactivated
46232          * Fires when a panel is activated. 
46233          * @param {Roo.LayoutRegion} this
46234          * @param {Roo.ContentPanel} panel The activated panel
46235          */
46236         "panelactivated" : true,
46237         /**
46238          * @event resized
46239          * Fires when the user resizes this region. 
46240          * @param {Roo.LayoutRegion} this
46241          * @param {Number} newSize The new size (width for east/west, height for north/south)
46242          */
46243         "resized" : true
46244     };
46245     /** A collection of panels in this region. @type Roo.util.MixedCollection */
46246     this.panels = new Roo.util.MixedCollection();
46247     this.panels.getKey = this.getPanelId.createDelegate(this);
46248     this.box = null;
46249     this.activePanel = null;
46250     // ensure listeners are added...
46251     
46252     if (config.listeners || config.events) {
46253         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
46254             listeners : config.listeners || {},
46255             events : config.events || {}
46256         });
46257     }
46258     
46259     if(skipConfig !== true){
46260         this.applyConfig(config);
46261     }
46262 };
46263
46264 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
46265     getPanelId : function(p){
46266         return p.getId();
46267     },
46268     
46269     applyConfig : function(config){
46270         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
46271         this.config = config;
46272         
46273     },
46274     
46275     /**
46276      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
46277      * the width, for horizontal (north, south) the height.
46278      * @param {Number} newSize The new width or height
46279      */
46280     resizeTo : function(newSize){
46281         var el = this.el ? this.el :
46282                  (this.activePanel ? this.activePanel.getEl() : null);
46283         if(el){
46284             switch(this.position){
46285                 case "east":
46286                 case "west":
46287                     el.setWidth(newSize);
46288                     this.fireEvent("resized", this, newSize);
46289                 break;
46290                 case "north":
46291                 case "south":
46292                     el.setHeight(newSize);
46293                     this.fireEvent("resized", this, newSize);
46294                 break;                
46295             }
46296         }
46297     },
46298     
46299     getBox : function(){
46300         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
46301     },
46302     
46303     getMargins : function(){
46304         return this.margins;
46305     },
46306     
46307     updateBox : function(box){
46308         this.box = box;
46309         var el = this.activePanel.getEl();
46310         el.dom.style.left = box.x + "px";
46311         el.dom.style.top = box.y + "px";
46312         this.activePanel.setSize(box.width, box.height);
46313     },
46314     
46315     /**
46316      * Returns the container element for this region.
46317      * @return {Roo.Element}
46318      */
46319     getEl : function(){
46320         return this.activePanel;
46321     },
46322     
46323     /**
46324      * Returns true if this region is currently visible.
46325      * @return {Boolean}
46326      */
46327     isVisible : function(){
46328         return this.activePanel ? true : false;
46329     },
46330     
46331     setActivePanel : function(panel){
46332         panel = this.getPanel(panel);
46333         if(this.activePanel && this.activePanel != panel){
46334             this.activePanel.setActiveState(false);
46335             this.activePanel.getEl().setLeftTop(-10000,-10000);
46336         }
46337         this.activePanel = panel;
46338         panel.setActiveState(true);
46339         if(this.box){
46340             panel.setSize(this.box.width, this.box.height);
46341         }
46342         this.fireEvent("panelactivated", this, panel);
46343         this.fireEvent("invalidated");
46344     },
46345     
46346     /**
46347      * Show the specified panel.
46348      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
46349      * @return {Roo.ContentPanel} The shown panel or null
46350      */
46351     showPanel : function(panel){
46352         if(panel = this.getPanel(panel)){
46353             this.setActivePanel(panel);
46354         }
46355         return panel;
46356     },
46357     
46358     /**
46359      * Get the active panel for this region.
46360      * @return {Roo.ContentPanel} The active panel or null
46361      */
46362     getActivePanel : function(){
46363         return this.activePanel;
46364     },
46365     
46366     /**
46367      * Add the passed ContentPanel(s)
46368      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
46369      * @return {Roo.ContentPanel} The panel added (if only one was added)
46370      */
46371     add : function(panel){
46372         if(arguments.length > 1){
46373             for(var i = 0, len = arguments.length; i < len; i++) {
46374                 this.add(arguments[i]);
46375             }
46376             return null;
46377         }
46378         if(this.hasPanel(panel)){
46379             this.showPanel(panel);
46380             return panel;
46381         }
46382         var el = panel.getEl();
46383         if(el.dom.parentNode != this.mgr.el.dom){
46384             this.mgr.el.dom.appendChild(el.dom);
46385         }
46386         if(panel.setRegion){
46387             panel.setRegion(this);
46388         }
46389         this.panels.add(panel);
46390         el.setStyle("position", "absolute");
46391         if(!panel.background){
46392             this.setActivePanel(panel);
46393             if(this.config.initialSize && this.panels.getCount()==1){
46394                 this.resizeTo(this.config.initialSize);
46395             }
46396         }
46397         this.fireEvent("paneladded", this, panel);
46398         return panel;
46399     },
46400     
46401     /**
46402      * Returns true if the panel is in this region.
46403      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
46404      * @return {Boolean}
46405      */
46406     hasPanel : function(panel){
46407         if(typeof panel == "object"){ // must be panel obj
46408             panel = panel.getId();
46409         }
46410         return this.getPanel(panel) ? true : false;
46411     },
46412     
46413     /**
46414      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
46415      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
46416      * @param {Boolean} preservePanel Overrides the config preservePanel option
46417      * @return {Roo.ContentPanel} The panel that was removed
46418      */
46419     remove : function(panel, preservePanel){
46420         panel = this.getPanel(panel);
46421         if(!panel){
46422             return null;
46423         }
46424         var e = {};
46425         this.fireEvent("beforeremove", this, panel, e);
46426         if(e.cancel === true){
46427             return null;
46428         }
46429         var panelId = panel.getId();
46430         this.panels.removeKey(panelId);
46431         return panel;
46432     },
46433     
46434     /**
46435      * Returns the panel specified or null if it's not in this region.
46436      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
46437      * @return {Roo.ContentPanel}
46438      */
46439     getPanel : function(id){
46440         if(typeof id == "object"){ // must be panel obj
46441             return id;
46442         }
46443         return this.panels.get(id);
46444     },
46445     
46446     /**
46447      * Returns this regions position (north/south/east/west/center).
46448      * @return {String} 
46449      */
46450     getPosition: function(){
46451         return this.position;    
46452     }
46453 });/*
46454  * Based on:
46455  * Ext JS Library 1.1.1
46456  * Copyright(c) 2006-2007, Ext JS, LLC.
46457  *
46458  * Originally Released Under LGPL - original licence link has changed is not relivant.
46459  *
46460  * Fork - LGPL
46461  * <script type="text/javascript">
46462  */
46463  
46464 /**
46465  * @class Roo.LayoutRegion
46466  * @extends Roo.BasicLayoutRegion
46467  * This class represents a region in a layout manager.
46468  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
46469  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
46470  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
46471  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
46472  * @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})
46473  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
46474  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
46475  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
46476  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
46477  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
46478  * @cfg {String}    title           The title for the region (overrides panel titles)
46479  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
46480  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
46481  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
46482  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
46483  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
46484  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
46485  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
46486  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
46487  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
46488  * @cfg {Boolean}   showPin         True to show a pin button
46489  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
46490  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
46491  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
46492  * @cfg {Number}    width           For East/West panels
46493  * @cfg {Number}    height          For North/South panels
46494  * @cfg {Boolean}   split           To show the splitter
46495  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
46496  */
46497 Roo.LayoutRegion = function(mgr, config, pos){
46498     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
46499     var dh = Roo.DomHelper;
46500     /** This region's container element 
46501     * @type Roo.Element */
46502     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
46503     /** This region's title element 
46504     * @type Roo.Element */
46505
46506     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
46507         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
46508         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
46509     ]}, true);
46510     this.titleEl.enableDisplayMode();
46511     /** This region's title text element 
46512     * @type HTMLElement */
46513     this.titleTextEl = this.titleEl.dom.firstChild;
46514     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
46515     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
46516     this.closeBtn.enableDisplayMode();
46517     this.closeBtn.on("click", this.closeClicked, this);
46518     this.closeBtn.hide();
46519
46520     this.createBody(config);
46521     this.visible = true;
46522     this.collapsed = false;
46523
46524     if(config.hideWhenEmpty){
46525         this.hide();
46526         this.on("paneladded", this.validateVisibility, this);
46527         this.on("panelremoved", this.validateVisibility, this);
46528     }
46529     this.applyConfig(config);
46530 };
46531
46532 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
46533
46534     createBody : function(){
46535         /** This region's body element 
46536         * @type Roo.Element */
46537         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
46538     },
46539
46540     applyConfig : function(c){
46541         if(c.collapsible && this.position != "center" && !this.collapsedEl){
46542             var dh = Roo.DomHelper;
46543             if(c.titlebar !== false){
46544                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
46545                 this.collapseBtn.on("click", this.collapse, this);
46546                 this.collapseBtn.enableDisplayMode();
46547
46548                 if(c.showPin === true || this.showPin){
46549                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
46550                     this.stickBtn.enableDisplayMode();
46551                     this.stickBtn.on("click", this.expand, this);
46552                     this.stickBtn.hide();
46553                 }
46554             }
46555             /** This region's collapsed element
46556             * @type Roo.Element */
46557             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
46558                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
46559             ]}, true);
46560             if(c.floatable !== false){
46561                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
46562                this.collapsedEl.on("click", this.collapseClick, this);
46563             }
46564
46565             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
46566                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
46567                    id: "message", unselectable: "on", style:{"float":"left"}});
46568                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
46569              }
46570             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
46571             this.expandBtn.on("click", this.expand, this);
46572         }
46573         if(this.collapseBtn){
46574             this.collapseBtn.setVisible(c.collapsible == true);
46575         }
46576         this.cmargins = c.cmargins || this.cmargins ||
46577                          (this.position == "west" || this.position == "east" ?
46578                              {top: 0, left: 2, right:2, bottom: 0} :
46579                              {top: 2, left: 0, right:0, bottom: 2});
46580         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
46581         this.bottomTabs = c.tabPosition != "top";
46582         this.autoScroll = c.autoScroll || false;
46583         if(this.autoScroll){
46584             this.bodyEl.setStyle("overflow", "auto");
46585         }else{
46586             this.bodyEl.setStyle("overflow", "hidden");
46587         }
46588         //if(c.titlebar !== false){
46589             if((!c.titlebar && !c.title) || c.titlebar === false){
46590                 this.titleEl.hide();
46591             }else{
46592                 this.titleEl.show();
46593                 if(c.title){
46594                     this.titleTextEl.innerHTML = c.title;
46595                 }
46596             }
46597         //}
46598         this.duration = c.duration || .30;
46599         this.slideDuration = c.slideDuration || .45;
46600         this.config = c;
46601         if(c.collapsed){
46602             this.collapse(true);
46603         }
46604         if(c.hidden){
46605             this.hide();
46606         }
46607     },
46608     /**
46609      * Returns true if this region is currently visible.
46610      * @return {Boolean}
46611      */
46612     isVisible : function(){
46613         return this.visible;
46614     },
46615
46616     /**
46617      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
46618      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
46619      */
46620     setCollapsedTitle : function(title){
46621         title = title || "&#160;";
46622         if(this.collapsedTitleTextEl){
46623             this.collapsedTitleTextEl.innerHTML = title;
46624         }
46625     },
46626
46627     getBox : function(){
46628         var b;
46629         if(!this.collapsed){
46630             b = this.el.getBox(false, true);
46631         }else{
46632             b = this.collapsedEl.getBox(false, true);
46633         }
46634         return b;
46635     },
46636
46637     getMargins : function(){
46638         return this.collapsed ? this.cmargins : this.margins;
46639     },
46640
46641     highlight : function(){
46642         this.el.addClass("x-layout-panel-dragover");
46643     },
46644
46645     unhighlight : function(){
46646         this.el.removeClass("x-layout-panel-dragover");
46647     },
46648
46649     updateBox : function(box){
46650         this.box = box;
46651         if(!this.collapsed){
46652             this.el.dom.style.left = box.x + "px";
46653             this.el.dom.style.top = box.y + "px";
46654             this.updateBody(box.width, box.height);
46655         }else{
46656             this.collapsedEl.dom.style.left = box.x + "px";
46657             this.collapsedEl.dom.style.top = box.y + "px";
46658             this.collapsedEl.setSize(box.width, box.height);
46659         }
46660         if(this.tabs){
46661             this.tabs.autoSizeTabs();
46662         }
46663     },
46664
46665     updateBody : function(w, h){
46666         if(w !== null){
46667             this.el.setWidth(w);
46668             w -= this.el.getBorderWidth("rl");
46669             if(this.config.adjustments){
46670                 w += this.config.adjustments[0];
46671             }
46672         }
46673         if(h !== null){
46674             this.el.setHeight(h);
46675             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
46676             h -= this.el.getBorderWidth("tb");
46677             if(this.config.adjustments){
46678                 h += this.config.adjustments[1];
46679             }
46680             this.bodyEl.setHeight(h);
46681             if(this.tabs){
46682                 h = this.tabs.syncHeight(h);
46683             }
46684         }
46685         if(this.panelSize){
46686             w = w !== null ? w : this.panelSize.width;
46687             h = h !== null ? h : this.panelSize.height;
46688         }
46689         if(this.activePanel){
46690             var el = this.activePanel.getEl();
46691             w = w !== null ? w : el.getWidth();
46692             h = h !== null ? h : el.getHeight();
46693             this.panelSize = {width: w, height: h};
46694             this.activePanel.setSize(w, h);
46695         }
46696         if(Roo.isIE && this.tabs){
46697             this.tabs.el.repaint();
46698         }
46699     },
46700
46701     /**
46702      * Returns the container element for this region.
46703      * @return {Roo.Element}
46704      */
46705     getEl : function(){
46706         return this.el;
46707     },
46708
46709     /**
46710      * Hides this region.
46711      */
46712     hide : function(){
46713         if(!this.collapsed){
46714             this.el.dom.style.left = "-2000px";
46715             this.el.hide();
46716         }else{
46717             this.collapsedEl.dom.style.left = "-2000px";
46718             this.collapsedEl.hide();
46719         }
46720         this.visible = false;
46721         this.fireEvent("visibilitychange", this, false);
46722     },
46723
46724     /**
46725      * Shows this region if it was previously hidden.
46726      */
46727     show : function(){
46728         if(!this.collapsed){
46729             this.el.show();
46730         }else{
46731             this.collapsedEl.show();
46732         }
46733         this.visible = true;
46734         this.fireEvent("visibilitychange", this, true);
46735     },
46736
46737     closeClicked : function(){
46738         if(this.activePanel){
46739             this.remove(this.activePanel);
46740         }
46741     },
46742
46743     collapseClick : function(e){
46744         if(this.isSlid){
46745            e.stopPropagation();
46746            this.slideIn();
46747         }else{
46748            e.stopPropagation();
46749            this.slideOut();
46750         }
46751     },
46752
46753     /**
46754      * Collapses this region.
46755      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
46756      */
46757     collapse : function(skipAnim){
46758         if(this.collapsed) return;
46759         this.collapsed = true;
46760         if(this.split){
46761             this.split.el.hide();
46762         }
46763         if(this.config.animate && skipAnim !== true){
46764             this.fireEvent("invalidated", this);
46765             this.animateCollapse();
46766         }else{
46767             this.el.setLocation(-20000,-20000);
46768             this.el.hide();
46769             this.collapsedEl.show();
46770             this.fireEvent("collapsed", this);
46771             this.fireEvent("invalidated", this);
46772         }
46773     },
46774
46775     animateCollapse : function(){
46776         // overridden
46777     },
46778
46779     /**
46780      * Expands this region if it was previously collapsed.
46781      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
46782      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
46783      */
46784     expand : function(e, skipAnim){
46785         if(e) e.stopPropagation();
46786         if(!this.collapsed || this.el.hasActiveFx()) return;
46787         if(this.isSlid){
46788             this.afterSlideIn();
46789             skipAnim = true;
46790         }
46791         this.collapsed = false;
46792         if(this.config.animate && skipAnim !== true){
46793             this.animateExpand();
46794         }else{
46795             this.el.show();
46796             if(this.split){
46797                 this.split.el.show();
46798             }
46799             this.collapsedEl.setLocation(-2000,-2000);
46800             this.collapsedEl.hide();
46801             this.fireEvent("invalidated", this);
46802             this.fireEvent("expanded", this);
46803         }
46804     },
46805
46806     animateExpand : function(){
46807         // overridden
46808     },
46809
46810     initTabs : function()
46811     {
46812         this.bodyEl.setStyle("overflow", "hidden");
46813         var ts = new Roo.TabPanel(
46814                 this.bodyEl.dom,
46815                 {
46816                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
46817                     disableTooltips: this.config.disableTabTips,
46818                     toolbar : this.config.toolbar
46819                 }
46820         );
46821         if(this.config.hideTabs){
46822             ts.stripWrap.setDisplayed(false);
46823         }
46824         this.tabs = ts;
46825         ts.resizeTabs = this.config.resizeTabs === true;
46826         ts.minTabWidth = this.config.minTabWidth || 40;
46827         ts.maxTabWidth = this.config.maxTabWidth || 250;
46828         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
46829         ts.monitorResize = false;
46830         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
46831         ts.bodyEl.addClass('x-layout-tabs-body');
46832         this.panels.each(this.initPanelAsTab, this);
46833     },
46834
46835     initPanelAsTab : function(panel){
46836         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
46837                     this.config.closeOnTab && panel.isClosable());
46838         if(panel.tabTip !== undefined){
46839             ti.setTooltip(panel.tabTip);
46840         }
46841         ti.on("activate", function(){
46842               this.setActivePanel(panel);
46843         }, this);
46844         if(this.config.closeOnTab){
46845             ti.on("beforeclose", function(t, e){
46846                 e.cancel = true;
46847                 this.remove(panel);
46848             }, this);
46849         }
46850         return ti;
46851     },
46852
46853     updatePanelTitle : function(panel, title){
46854         if(this.activePanel == panel){
46855             this.updateTitle(title);
46856         }
46857         if(this.tabs){
46858             var ti = this.tabs.getTab(panel.getEl().id);
46859             ti.setText(title);
46860             if(panel.tabTip !== undefined){
46861                 ti.setTooltip(panel.tabTip);
46862             }
46863         }
46864     },
46865
46866     updateTitle : function(title){
46867         if(this.titleTextEl && !this.config.title){
46868             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
46869         }
46870     },
46871
46872     setActivePanel : function(panel){
46873         panel = this.getPanel(panel);
46874         if(this.activePanel && this.activePanel != panel){
46875             this.activePanel.setActiveState(false);
46876         }
46877         this.activePanel = panel;
46878         panel.setActiveState(true);
46879         if(this.panelSize){
46880             panel.setSize(this.panelSize.width, this.panelSize.height);
46881         }
46882         if(this.closeBtn){
46883             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
46884         }
46885         this.updateTitle(panel.getTitle());
46886         if(this.tabs){
46887             this.fireEvent("invalidated", this);
46888         }
46889         this.fireEvent("panelactivated", this, panel);
46890     },
46891
46892     /**
46893      * Shows the specified panel.
46894      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
46895      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
46896      */
46897     showPanel : function(panel){
46898         if(panel = this.getPanel(panel)){
46899             if(this.tabs){
46900                 var tab = this.tabs.getTab(panel.getEl().id);
46901                 if(tab.isHidden()){
46902                     this.tabs.unhideTab(tab.id);
46903                 }
46904                 tab.activate();
46905             }else{
46906                 this.setActivePanel(panel);
46907             }
46908         }
46909         return panel;
46910     },
46911
46912     /**
46913      * Get the active panel for this region.
46914      * @return {Roo.ContentPanel} The active panel or null
46915      */
46916     getActivePanel : function(){
46917         return this.activePanel;
46918     },
46919
46920     validateVisibility : function(){
46921         if(this.panels.getCount() < 1){
46922             this.updateTitle("&#160;");
46923             this.closeBtn.hide();
46924             this.hide();
46925         }else{
46926             if(!this.isVisible()){
46927                 this.show();
46928             }
46929         }
46930     },
46931
46932     /**
46933      * Adds the passed ContentPanel(s) to this region.
46934      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
46935      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
46936      */
46937     add : function(panel){
46938         if(arguments.length > 1){
46939             for(var i = 0, len = arguments.length; i < len; i++) {
46940                 this.add(arguments[i]);
46941             }
46942             return null;
46943         }
46944         if(this.hasPanel(panel)){
46945             this.showPanel(panel);
46946             return panel;
46947         }
46948         panel.setRegion(this);
46949         this.panels.add(panel);
46950         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
46951             this.bodyEl.dom.appendChild(panel.getEl().dom);
46952             if(panel.background !== true){
46953                 this.setActivePanel(panel);
46954             }
46955             this.fireEvent("paneladded", this, panel);
46956             return panel;
46957         }
46958         if(!this.tabs){
46959             this.initTabs();
46960         }else{
46961             this.initPanelAsTab(panel);
46962         }
46963         if(panel.background !== true){
46964             this.tabs.activate(panel.getEl().id);
46965         }
46966         this.fireEvent("paneladded", this, panel);
46967         return panel;
46968     },
46969
46970     /**
46971      * Hides the tab for the specified panel.
46972      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46973      */
46974     hidePanel : function(panel){
46975         if(this.tabs && (panel = this.getPanel(panel))){
46976             this.tabs.hideTab(panel.getEl().id);
46977         }
46978     },
46979
46980     /**
46981      * Unhides the tab for a previously hidden panel.
46982      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46983      */
46984     unhidePanel : function(panel){
46985         if(this.tabs && (panel = this.getPanel(panel))){
46986             this.tabs.unhideTab(panel.getEl().id);
46987         }
46988     },
46989
46990     clearPanels : function(){
46991         while(this.panels.getCount() > 0){
46992              this.remove(this.panels.first());
46993         }
46994     },
46995
46996     /**
46997      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
46998      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46999      * @param {Boolean} preservePanel Overrides the config preservePanel option
47000      * @return {Roo.ContentPanel} The panel that was removed
47001      */
47002     remove : function(panel, preservePanel){
47003         panel = this.getPanel(panel);
47004         if(!panel){
47005             return null;
47006         }
47007         var e = {};
47008         this.fireEvent("beforeremove", this, panel, e);
47009         if(e.cancel === true){
47010             return null;
47011         }
47012         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
47013         var panelId = panel.getId();
47014         this.panels.removeKey(panelId);
47015         if(preservePanel){
47016             document.body.appendChild(panel.getEl().dom);
47017         }
47018         if(this.tabs){
47019             this.tabs.removeTab(panel.getEl().id);
47020         }else if (!preservePanel){
47021             this.bodyEl.dom.removeChild(panel.getEl().dom);
47022         }
47023         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
47024             var p = this.panels.first();
47025             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
47026             tempEl.appendChild(p.getEl().dom);
47027             this.bodyEl.update("");
47028             this.bodyEl.dom.appendChild(p.getEl().dom);
47029             tempEl = null;
47030             this.updateTitle(p.getTitle());
47031             this.tabs = null;
47032             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
47033             this.setActivePanel(p);
47034         }
47035         panel.setRegion(null);
47036         if(this.activePanel == panel){
47037             this.activePanel = null;
47038         }
47039         if(this.config.autoDestroy !== false && preservePanel !== true){
47040             try{panel.destroy();}catch(e){}
47041         }
47042         this.fireEvent("panelremoved", this, panel);
47043         return panel;
47044     },
47045
47046     /**
47047      * Returns the TabPanel component used by this region
47048      * @return {Roo.TabPanel}
47049      */
47050     getTabs : function(){
47051         return this.tabs;
47052     },
47053
47054     createTool : function(parentEl, className){
47055         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
47056             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
47057         btn.addClassOnOver("x-layout-tools-button-over");
47058         return btn;
47059     }
47060 });/*
47061  * Based on:
47062  * Ext JS Library 1.1.1
47063  * Copyright(c) 2006-2007, Ext JS, LLC.
47064  *
47065  * Originally Released Under LGPL - original licence link has changed is not relivant.
47066  *
47067  * Fork - LGPL
47068  * <script type="text/javascript">
47069  */
47070  
47071
47072
47073 /**
47074  * @class Roo.SplitLayoutRegion
47075  * @extends Roo.LayoutRegion
47076  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
47077  */
47078 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
47079     this.cursor = cursor;
47080     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
47081 };
47082
47083 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
47084     splitTip : "Drag to resize.",
47085     collapsibleSplitTip : "Drag to resize. Double click to hide.",
47086     useSplitTips : false,
47087
47088     applyConfig : function(config){
47089         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
47090         if(config.split){
47091             if(!this.split){
47092                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
47093                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
47094                 /** The SplitBar for this region 
47095                 * @type Roo.SplitBar */
47096                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
47097                 this.split.on("moved", this.onSplitMove, this);
47098                 this.split.useShim = config.useShim === true;
47099                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
47100                 if(this.useSplitTips){
47101                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
47102                 }
47103                 if(config.collapsible){
47104                     this.split.el.on("dblclick", this.collapse,  this);
47105                 }
47106             }
47107             if(typeof config.minSize != "undefined"){
47108                 this.split.minSize = config.minSize;
47109             }
47110             if(typeof config.maxSize != "undefined"){
47111                 this.split.maxSize = config.maxSize;
47112             }
47113             if(config.hideWhenEmpty || config.hidden || config.collapsed){
47114                 this.hideSplitter();
47115             }
47116         }
47117     },
47118
47119     getHMaxSize : function(){
47120          var cmax = this.config.maxSize || 10000;
47121          var center = this.mgr.getRegion("center");
47122          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
47123     },
47124
47125     getVMaxSize : function(){
47126          var cmax = this.config.maxSize || 10000;
47127          var center = this.mgr.getRegion("center");
47128          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
47129     },
47130
47131     onSplitMove : function(split, newSize){
47132         this.fireEvent("resized", this, newSize);
47133     },
47134     
47135     /** 
47136      * Returns the {@link Roo.SplitBar} for this region.
47137      * @return {Roo.SplitBar}
47138      */
47139     getSplitBar : function(){
47140         return this.split;
47141     },
47142     
47143     hide : function(){
47144         this.hideSplitter();
47145         Roo.SplitLayoutRegion.superclass.hide.call(this);
47146     },
47147
47148     hideSplitter : function(){
47149         if(this.split){
47150             this.split.el.setLocation(-2000,-2000);
47151             this.split.el.hide();
47152         }
47153     },
47154
47155     show : function(){
47156         if(this.split){
47157             this.split.el.show();
47158         }
47159         Roo.SplitLayoutRegion.superclass.show.call(this);
47160     },
47161     
47162     beforeSlide: function(){
47163         if(Roo.isGecko){// firefox overflow auto bug workaround
47164             this.bodyEl.clip();
47165             if(this.tabs) this.tabs.bodyEl.clip();
47166             if(this.activePanel){
47167                 this.activePanel.getEl().clip();
47168                 
47169                 if(this.activePanel.beforeSlide){
47170                     this.activePanel.beforeSlide();
47171                 }
47172             }
47173         }
47174     },
47175     
47176     afterSlide : function(){
47177         if(Roo.isGecko){// firefox overflow auto bug workaround
47178             this.bodyEl.unclip();
47179             if(this.tabs) this.tabs.bodyEl.unclip();
47180             if(this.activePanel){
47181                 this.activePanel.getEl().unclip();
47182                 if(this.activePanel.afterSlide){
47183                     this.activePanel.afterSlide();
47184                 }
47185             }
47186         }
47187     },
47188
47189     initAutoHide : function(){
47190         if(this.autoHide !== false){
47191             if(!this.autoHideHd){
47192                 var st = new Roo.util.DelayedTask(this.slideIn, this);
47193                 this.autoHideHd = {
47194                     "mouseout": function(e){
47195                         if(!e.within(this.el, true)){
47196                             st.delay(500);
47197                         }
47198                     },
47199                     "mouseover" : function(e){
47200                         st.cancel();
47201                     },
47202                     scope : this
47203                 };
47204             }
47205             this.el.on(this.autoHideHd);
47206         }
47207     },
47208
47209     clearAutoHide : function(){
47210         if(this.autoHide !== false){
47211             this.el.un("mouseout", this.autoHideHd.mouseout);
47212             this.el.un("mouseover", this.autoHideHd.mouseover);
47213         }
47214     },
47215
47216     clearMonitor : function(){
47217         Roo.get(document).un("click", this.slideInIf, this);
47218     },
47219
47220     // these names are backwards but not changed for compat
47221     slideOut : function(){
47222         if(this.isSlid || this.el.hasActiveFx()){
47223             return;
47224         }
47225         this.isSlid = true;
47226         if(this.collapseBtn){
47227             this.collapseBtn.hide();
47228         }
47229         this.closeBtnState = this.closeBtn.getStyle('display');
47230         this.closeBtn.hide();
47231         if(this.stickBtn){
47232             this.stickBtn.show();
47233         }
47234         this.el.show();
47235         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
47236         this.beforeSlide();
47237         this.el.setStyle("z-index", 10001);
47238         this.el.slideIn(this.getSlideAnchor(), {
47239             callback: function(){
47240                 this.afterSlide();
47241                 this.initAutoHide();
47242                 Roo.get(document).on("click", this.slideInIf, this);
47243                 this.fireEvent("slideshow", this);
47244             },
47245             scope: this,
47246             block: true
47247         });
47248     },
47249
47250     afterSlideIn : function(){
47251         this.clearAutoHide();
47252         this.isSlid = false;
47253         this.clearMonitor();
47254         this.el.setStyle("z-index", "");
47255         if(this.collapseBtn){
47256             this.collapseBtn.show();
47257         }
47258         this.closeBtn.setStyle('display', this.closeBtnState);
47259         if(this.stickBtn){
47260             this.stickBtn.hide();
47261         }
47262         this.fireEvent("slidehide", this);
47263     },
47264
47265     slideIn : function(cb){
47266         if(!this.isSlid || this.el.hasActiveFx()){
47267             Roo.callback(cb);
47268             return;
47269         }
47270         this.isSlid = false;
47271         this.beforeSlide();
47272         this.el.slideOut(this.getSlideAnchor(), {
47273             callback: function(){
47274                 this.el.setLeftTop(-10000, -10000);
47275                 this.afterSlide();
47276                 this.afterSlideIn();
47277                 Roo.callback(cb);
47278             },
47279             scope: this,
47280             block: true
47281         });
47282     },
47283     
47284     slideInIf : function(e){
47285         if(!e.within(this.el)){
47286             this.slideIn();
47287         }
47288     },
47289
47290     animateCollapse : function(){
47291         this.beforeSlide();
47292         this.el.setStyle("z-index", 20000);
47293         var anchor = this.getSlideAnchor();
47294         this.el.slideOut(anchor, {
47295             callback : function(){
47296                 this.el.setStyle("z-index", "");
47297                 this.collapsedEl.slideIn(anchor, {duration:.3});
47298                 this.afterSlide();
47299                 this.el.setLocation(-10000,-10000);
47300                 this.el.hide();
47301                 this.fireEvent("collapsed", this);
47302             },
47303             scope: this,
47304             block: true
47305         });
47306     },
47307
47308     animateExpand : function(){
47309         this.beforeSlide();
47310         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
47311         this.el.setStyle("z-index", 20000);
47312         this.collapsedEl.hide({
47313             duration:.1
47314         });
47315         this.el.slideIn(this.getSlideAnchor(), {
47316             callback : function(){
47317                 this.el.setStyle("z-index", "");
47318                 this.afterSlide();
47319                 if(this.split){
47320                     this.split.el.show();
47321                 }
47322                 this.fireEvent("invalidated", this);
47323                 this.fireEvent("expanded", this);
47324             },
47325             scope: this,
47326             block: true
47327         });
47328     },
47329
47330     anchors : {
47331         "west" : "left",
47332         "east" : "right",
47333         "north" : "top",
47334         "south" : "bottom"
47335     },
47336
47337     sanchors : {
47338         "west" : "l",
47339         "east" : "r",
47340         "north" : "t",
47341         "south" : "b"
47342     },
47343
47344     canchors : {
47345         "west" : "tl-tr",
47346         "east" : "tr-tl",
47347         "north" : "tl-bl",
47348         "south" : "bl-tl"
47349     },
47350
47351     getAnchor : function(){
47352         return this.anchors[this.position];
47353     },
47354
47355     getCollapseAnchor : function(){
47356         return this.canchors[this.position];
47357     },
47358
47359     getSlideAnchor : function(){
47360         return this.sanchors[this.position];
47361     },
47362
47363     getAlignAdj : function(){
47364         var cm = this.cmargins;
47365         switch(this.position){
47366             case "west":
47367                 return [0, 0];
47368             break;
47369             case "east":
47370                 return [0, 0];
47371             break;
47372             case "north":
47373                 return [0, 0];
47374             break;
47375             case "south":
47376                 return [0, 0];
47377             break;
47378         }
47379     },
47380
47381     getExpandAdj : function(){
47382         var c = this.collapsedEl, cm = this.cmargins;
47383         switch(this.position){
47384             case "west":
47385                 return [-(cm.right+c.getWidth()+cm.left), 0];
47386             break;
47387             case "east":
47388                 return [cm.right+c.getWidth()+cm.left, 0];
47389             break;
47390             case "north":
47391                 return [0, -(cm.top+cm.bottom+c.getHeight())];
47392             break;
47393             case "south":
47394                 return [0, cm.top+cm.bottom+c.getHeight()];
47395             break;
47396         }
47397     }
47398 });/*
47399  * Based on:
47400  * Ext JS Library 1.1.1
47401  * Copyright(c) 2006-2007, Ext JS, LLC.
47402  *
47403  * Originally Released Under LGPL - original licence link has changed is not relivant.
47404  *
47405  * Fork - LGPL
47406  * <script type="text/javascript">
47407  */
47408 /*
47409  * These classes are private internal classes
47410  */
47411 Roo.CenterLayoutRegion = function(mgr, config){
47412     Roo.LayoutRegion.call(this, mgr, config, "center");
47413     this.visible = true;
47414     this.minWidth = config.minWidth || 20;
47415     this.minHeight = config.minHeight || 20;
47416 };
47417
47418 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
47419     hide : function(){
47420         // center panel can't be hidden
47421     },
47422     
47423     show : function(){
47424         // center panel can't be hidden
47425     },
47426     
47427     getMinWidth: function(){
47428         return this.minWidth;
47429     },
47430     
47431     getMinHeight: function(){
47432         return this.minHeight;
47433     }
47434 });
47435
47436
47437 Roo.NorthLayoutRegion = function(mgr, config){
47438     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
47439     if(this.split){
47440         this.split.placement = Roo.SplitBar.TOP;
47441         this.split.orientation = Roo.SplitBar.VERTICAL;
47442         this.split.el.addClass("x-layout-split-v");
47443     }
47444     var size = config.initialSize || config.height;
47445     if(typeof size != "undefined"){
47446         this.el.setHeight(size);
47447     }
47448 };
47449 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
47450     orientation: Roo.SplitBar.VERTICAL,
47451     getBox : function(){
47452         if(this.collapsed){
47453             return this.collapsedEl.getBox();
47454         }
47455         var box = this.el.getBox();
47456         if(this.split){
47457             box.height += this.split.el.getHeight();
47458         }
47459         return box;
47460     },
47461     
47462     updateBox : function(box){
47463         if(this.split && !this.collapsed){
47464             box.height -= this.split.el.getHeight();
47465             this.split.el.setLeft(box.x);
47466             this.split.el.setTop(box.y+box.height);
47467             this.split.el.setWidth(box.width);
47468         }
47469         if(this.collapsed){
47470             this.updateBody(box.width, null);
47471         }
47472         Roo.LayoutRegion.prototype.updateBox.call(this, box);
47473     }
47474 });
47475
47476 Roo.SouthLayoutRegion = function(mgr, config){
47477     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
47478     if(this.split){
47479         this.split.placement = Roo.SplitBar.BOTTOM;
47480         this.split.orientation = Roo.SplitBar.VERTICAL;
47481         this.split.el.addClass("x-layout-split-v");
47482     }
47483     var size = config.initialSize || config.height;
47484     if(typeof size != "undefined"){
47485         this.el.setHeight(size);
47486     }
47487 };
47488 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
47489     orientation: Roo.SplitBar.VERTICAL,
47490     getBox : function(){
47491         if(this.collapsed){
47492             return this.collapsedEl.getBox();
47493         }
47494         var box = this.el.getBox();
47495         if(this.split){
47496             var sh = this.split.el.getHeight();
47497             box.height += sh;
47498             box.y -= sh;
47499         }
47500         return box;
47501     },
47502     
47503     updateBox : function(box){
47504         if(this.split && !this.collapsed){
47505             var sh = this.split.el.getHeight();
47506             box.height -= sh;
47507             box.y += sh;
47508             this.split.el.setLeft(box.x);
47509             this.split.el.setTop(box.y-sh);
47510             this.split.el.setWidth(box.width);
47511         }
47512         if(this.collapsed){
47513             this.updateBody(box.width, null);
47514         }
47515         Roo.LayoutRegion.prototype.updateBox.call(this, box);
47516     }
47517 });
47518
47519 Roo.EastLayoutRegion = function(mgr, config){
47520     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
47521     if(this.split){
47522         this.split.placement = Roo.SplitBar.RIGHT;
47523         this.split.orientation = Roo.SplitBar.HORIZONTAL;
47524         this.split.el.addClass("x-layout-split-h");
47525     }
47526     var size = config.initialSize || config.width;
47527     if(typeof size != "undefined"){
47528         this.el.setWidth(size);
47529     }
47530 };
47531 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
47532     orientation: Roo.SplitBar.HORIZONTAL,
47533     getBox : function(){
47534         if(this.collapsed){
47535             return this.collapsedEl.getBox();
47536         }
47537         var box = this.el.getBox();
47538         if(this.split){
47539             var sw = this.split.el.getWidth();
47540             box.width += sw;
47541             box.x -= sw;
47542         }
47543         return box;
47544     },
47545
47546     updateBox : function(box){
47547         if(this.split && !this.collapsed){
47548             var sw = this.split.el.getWidth();
47549             box.width -= sw;
47550             this.split.el.setLeft(box.x);
47551             this.split.el.setTop(box.y);
47552             this.split.el.setHeight(box.height);
47553             box.x += sw;
47554         }
47555         if(this.collapsed){
47556             this.updateBody(null, box.height);
47557         }
47558         Roo.LayoutRegion.prototype.updateBox.call(this, box);
47559     }
47560 });
47561
47562 Roo.WestLayoutRegion = function(mgr, config){
47563     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
47564     if(this.split){
47565         this.split.placement = Roo.SplitBar.LEFT;
47566         this.split.orientation = Roo.SplitBar.HORIZONTAL;
47567         this.split.el.addClass("x-layout-split-h");
47568     }
47569     var size = config.initialSize || config.width;
47570     if(typeof size != "undefined"){
47571         this.el.setWidth(size);
47572     }
47573 };
47574 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
47575     orientation: Roo.SplitBar.HORIZONTAL,
47576     getBox : function(){
47577         if(this.collapsed){
47578             return this.collapsedEl.getBox();
47579         }
47580         var box = this.el.getBox();
47581         if(this.split){
47582             box.width += this.split.el.getWidth();
47583         }
47584         return box;
47585     },
47586     
47587     updateBox : function(box){
47588         if(this.split && !this.collapsed){
47589             var sw = this.split.el.getWidth();
47590             box.width -= sw;
47591             this.split.el.setLeft(box.x+box.width);
47592             this.split.el.setTop(box.y);
47593             this.split.el.setHeight(box.height);
47594         }
47595         if(this.collapsed){
47596             this.updateBody(null, box.height);
47597         }
47598         Roo.LayoutRegion.prototype.updateBox.call(this, box);
47599     }
47600 });
47601 /*
47602  * Based on:
47603  * Ext JS Library 1.1.1
47604  * Copyright(c) 2006-2007, Ext JS, LLC.
47605  *
47606  * Originally Released Under LGPL - original licence link has changed is not relivant.
47607  *
47608  * Fork - LGPL
47609  * <script type="text/javascript">
47610  */
47611  
47612  
47613 /*
47614  * Private internal class for reading and applying state
47615  */
47616 Roo.LayoutStateManager = function(layout){
47617      // default empty state
47618      this.state = {
47619         north: {},
47620         south: {},
47621         east: {},
47622         west: {}       
47623     };
47624 };
47625
47626 Roo.LayoutStateManager.prototype = {
47627     init : function(layout, provider){
47628         this.provider = provider;
47629         var state = provider.get(layout.id+"-layout-state");
47630         if(state){
47631             var wasUpdating = layout.isUpdating();
47632             if(!wasUpdating){
47633                 layout.beginUpdate();
47634             }
47635             for(var key in state){
47636                 if(typeof state[key] != "function"){
47637                     var rstate = state[key];
47638                     var r = layout.getRegion(key);
47639                     if(r && rstate){
47640                         if(rstate.size){
47641                             r.resizeTo(rstate.size);
47642                         }
47643                         if(rstate.collapsed == true){
47644                             r.collapse(true);
47645                         }else{
47646                             r.expand(null, true);
47647                         }
47648                     }
47649                 }
47650             }
47651             if(!wasUpdating){
47652                 layout.endUpdate();
47653             }
47654             this.state = state; 
47655         }
47656         this.layout = layout;
47657         layout.on("regionresized", this.onRegionResized, this);
47658         layout.on("regioncollapsed", this.onRegionCollapsed, this);
47659         layout.on("regionexpanded", this.onRegionExpanded, this);
47660     },
47661     
47662     storeState : function(){
47663         this.provider.set(this.layout.id+"-layout-state", this.state);
47664     },
47665     
47666     onRegionResized : function(region, newSize){
47667         this.state[region.getPosition()].size = newSize;
47668         this.storeState();
47669     },
47670     
47671     onRegionCollapsed : function(region){
47672         this.state[region.getPosition()].collapsed = true;
47673         this.storeState();
47674     },
47675     
47676     onRegionExpanded : function(region){
47677         this.state[region.getPosition()].collapsed = false;
47678         this.storeState();
47679     }
47680 };/*
47681  * Based on:
47682  * Ext JS Library 1.1.1
47683  * Copyright(c) 2006-2007, Ext JS, LLC.
47684  *
47685  * Originally Released Under LGPL - original licence link has changed is not relivant.
47686  *
47687  * Fork - LGPL
47688  * <script type="text/javascript">
47689  */
47690 /**
47691  * @class Roo.ContentPanel
47692  * @extends Roo.util.Observable
47693  * A basic ContentPanel element.
47694  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
47695  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
47696  * @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
47697  * @cfg {Boolean}   closable      True if the panel can be closed/removed
47698  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
47699  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
47700  * @cfg {Toolbar}   toolbar       A toolbar for this panel
47701  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
47702  * @cfg {String} title          The title for this panel
47703  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
47704  * @cfg {String} url            Calls {@link #setUrl} with this value
47705  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
47706  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
47707  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
47708  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
47709
47710  * @constructor
47711  * Create a new ContentPanel.
47712  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
47713  * @param {String/Object} config A string to set only the title or a config object
47714  * @param {String} content (optional) Set the HTML content for this panel
47715  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
47716  */
47717 Roo.ContentPanel = function(el, config, content){
47718     
47719      
47720     /*
47721     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
47722         config = el;
47723         el = Roo.id();
47724     }
47725     if (config && config.parentLayout) { 
47726         el = config.parentLayout.el.createChild(); 
47727     }
47728     */
47729     if(el.autoCreate){ // xtype is available if this is called from factory
47730         config = el;
47731         el = Roo.id();
47732     }
47733     this.el = Roo.get(el);
47734     if(!this.el && config && config.autoCreate){
47735         if(typeof config.autoCreate == "object"){
47736             if(!config.autoCreate.id){
47737                 config.autoCreate.id = config.id||el;
47738             }
47739             this.el = Roo.DomHelper.append(document.body,
47740                         config.autoCreate, true);
47741         }else{
47742             this.el = Roo.DomHelper.append(document.body,
47743                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
47744         }
47745     }
47746     this.closable = false;
47747     this.loaded = false;
47748     this.active = false;
47749     if(typeof config == "string"){
47750         this.title = config;
47751     }else{
47752         Roo.apply(this, config);
47753     }
47754     
47755     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
47756         this.wrapEl = this.el.wrap();
47757         this.toolbar.container = this.el.insertSibling(false, 'before');
47758         this.toolbar = new Roo.Toolbar(this.toolbar);
47759     }
47760     
47761     
47762     
47763     if(this.resizeEl){
47764         this.resizeEl = Roo.get(this.resizeEl, true);
47765     }else{
47766         this.resizeEl = this.el;
47767     }
47768     this.addEvents({
47769         /**
47770          * @event activate
47771          * Fires when this panel is activated. 
47772          * @param {Roo.ContentPanel} this
47773          */
47774         "activate" : true,
47775         /**
47776          * @event deactivate
47777          * Fires when this panel is activated. 
47778          * @param {Roo.ContentPanel} this
47779          */
47780         "deactivate" : true,
47781
47782         /**
47783          * @event resize
47784          * Fires when this panel is resized if fitToFrame is true.
47785          * @param {Roo.ContentPanel} this
47786          * @param {Number} width The width after any component adjustments
47787          * @param {Number} height The height after any component adjustments
47788          */
47789         "resize" : true,
47790         
47791          /**
47792          * @event render
47793          * Fires when this tab is created
47794          * @param {Roo.ContentPanel} this
47795          */
47796         "render" : true
47797         
47798         
47799         
47800     });
47801     if(this.autoScroll){
47802         this.resizeEl.setStyle("overflow", "auto");
47803     } else {
47804         // fix randome scrolling
47805         this.el.on('scroll', function() {
47806             Roo.log('fix random scolling');
47807             this.scrollTo('top',0); 
47808         });
47809     }
47810     content = content || this.content;
47811     if(content){
47812         this.setContent(content);
47813     }
47814     if(config && config.url){
47815         this.setUrl(this.url, this.params, this.loadOnce);
47816     }
47817     
47818     
47819     
47820     Roo.ContentPanel.superclass.constructor.call(this);
47821     
47822     this.fireEvent('render', this);
47823 };
47824
47825 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
47826     tabTip:'',
47827     setRegion : function(region){
47828         this.region = region;
47829         if(region){
47830            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
47831         }else{
47832            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
47833         } 
47834     },
47835     
47836     /**
47837      * Returns the toolbar for this Panel if one was configured. 
47838      * @return {Roo.Toolbar} 
47839      */
47840     getToolbar : function(){
47841         return this.toolbar;
47842     },
47843     
47844     setActiveState : function(active){
47845         this.active = active;
47846         if(!active){
47847             this.fireEvent("deactivate", this);
47848         }else{
47849             this.fireEvent("activate", this);
47850         }
47851     },
47852     /**
47853      * Updates this panel's element
47854      * @param {String} content The new content
47855      * @param {Boolean} loadScripts (optional) true to look for and process scripts
47856     */
47857     setContent : function(content, loadScripts){
47858         this.el.update(content, loadScripts);
47859     },
47860
47861     ignoreResize : function(w, h){
47862         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
47863             return true;
47864         }else{
47865             this.lastSize = {width: w, height: h};
47866             return false;
47867         }
47868     },
47869     /**
47870      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
47871      * @return {Roo.UpdateManager} The UpdateManager
47872      */
47873     getUpdateManager : function(){
47874         return this.el.getUpdateManager();
47875     },
47876      /**
47877      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
47878      * @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:
47879 <pre><code>
47880 panel.load({
47881     url: "your-url.php",
47882     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
47883     callback: yourFunction,
47884     scope: yourObject, //(optional scope)
47885     discardUrl: false,
47886     nocache: false,
47887     text: "Loading...",
47888     timeout: 30,
47889     scripts: false
47890 });
47891 </code></pre>
47892      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
47893      * 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.
47894      * @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}
47895      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
47896      * @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.
47897      * @return {Roo.ContentPanel} this
47898      */
47899     load : function(){
47900         var um = this.el.getUpdateManager();
47901         um.update.apply(um, arguments);
47902         return this;
47903     },
47904
47905
47906     /**
47907      * 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.
47908      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
47909      * @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)
47910      * @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)
47911      * @return {Roo.UpdateManager} The UpdateManager
47912      */
47913     setUrl : function(url, params, loadOnce){
47914         if(this.refreshDelegate){
47915             this.removeListener("activate", this.refreshDelegate);
47916         }
47917         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47918         this.on("activate", this.refreshDelegate);
47919         return this.el.getUpdateManager();
47920     },
47921     
47922     _handleRefresh : function(url, params, loadOnce){
47923         if(!loadOnce || !this.loaded){
47924             var updater = this.el.getUpdateManager();
47925             updater.update(url, params, this._setLoaded.createDelegate(this));
47926         }
47927     },
47928     
47929     _setLoaded : function(){
47930         this.loaded = true;
47931     }, 
47932     
47933     /**
47934      * Returns this panel's id
47935      * @return {String} 
47936      */
47937     getId : function(){
47938         return this.el.id;
47939     },
47940     
47941     /** 
47942      * Returns this panel's element - used by regiosn to add.
47943      * @return {Roo.Element} 
47944      */
47945     getEl : function(){
47946         return this.wrapEl || this.el;
47947     },
47948     
47949     adjustForComponents : function(width, height){
47950         if(this.resizeEl != this.el){
47951             width -= this.el.getFrameWidth('lr');
47952             height -= this.el.getFrameWidth('tb');
47953         }
47954         if(this.toolbar){
47955             var te = this.toolbar.getEl();
47956             height -= te.getHeight();
47957             te.setWidth(width);
47958         }
47959         if(this.adjustments){
47960             width += this.adjustments[0];
47961             height += this.adjustments[1];
47962         }
47963         return {"width": width, "height": height};
47964     },
47965     
47966     setSize : function(width, height){
47967         if(this.fitToFrame && !this.ignoreResize(width, height)){
47968             if(this.fitContainer && this.resizeEl != this.el){
47969                 this.el.setSize(width, height);
47970             }
47971             var size = this.adjustForComponents(width, height);
47972             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
47973             this.fireEvent('resize', this, size.width, size.height);
47974         }
47975     },
47976     
47977     /**
47978      * Returns this panel's title
47979      * @return {String} 
47980      */
47981     getTitle : function(){
47982         return this.title;
47983     },
47984     
47985     /**
47986      * Set this panel's title
47987      * @param {String} title
47988      */
47989     setTitle : function(title){
47990         this.title = title;
47991         if(this.region){
47992             this.region.updatePanelTitle(this, title);
47993         }
47994     },
47995     
47996     /**
47997      * Returns true is this panel was configured to be closable
47998      * @return {Boolean} 
47999      */
48000     isClosable : function(){
48001         return this.closable;
48002     },
48003     
48004     beforeSlide : function(){
48005         this.el.clip();
48006         this.resizeEl.clip();
48007     },
48008     
48009     afterSlide : function(){
48010         this.el.unclip();
48011         this.resizeEl.unclip();
48012     },
48013     
48014     /**
48015      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
48016      *   Will fail silently if the {@link #setUrl} method has not been called.
48017      *   This does not activate the panel, just updates its content.
48018      */
48019     refresh : function(){
48020         if(this.refreshDelegate){
48021            this.loaded = false;
48022            this.refreshDelegate();
48023         }
48024     },
48025     
48026     /**
48027      * Destroys this panel
48028      */
48029     destroy : function(){
48030         this.el.removeAllListeners();
48031         var tempEl = document.createElement("span");
48032         tempEl.appendChild(this.el.dom);
48033         tempEl.innerHTML = "";
48034         this.el.remove();
48035         this.el = null;
48036     },
48037     
48038     /**
48039      * form - if the content panel contains a form - this is a reference to it.
48040      * @type {Roo.form.Form}
48041      */
48042     form : false,
48043     /**
48044      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
48045      *    This contains a reference to it.
48046      * @type {Roo.View}
48047      */
48048     view : false,
48049     
48050       /**
48051      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
48052      * <pre><code>
48053
48054 layout.addxtype({
48055        xtype : 'Form',
48056        items: [ .... ]
48057    }
48058 );
48059
48060 </code></pre>
48061      * @param {Object} cfg Xtype definition of item to add.
48062      */
48063     
48064     addxtype : function(cfg) {
48065         // add form..
48066         if (cfg.xtype.match(/^Form$/)) {
48067             var el = this.el.createChild();
48068
48069             this.form = new  Roo.form.Form(cfg);
48070             
48071             
48072             if ( this.form.allItems.length) this.form.render(el.dom);
48073             return this.form;
48074         }
48075         // should only have one of theses..
48076         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
48077             // views..
48078             cfg.el = this.el.appendChild(document.createElement("div"));
48079             // factory?
48080             
48081             var ret = new Roo.factory(cfg);
48082             ret.render && ret.render(false, ''); // render blank..
48083             this.view = ret;
48084             return ret;
48085         }
48086         return false;
48087     }
48088 });
48089
48090 /**
48091  * @class Roo.GridPanel
48092  * @extends Roo.ContentPanel
48093  * @constructor
48094  * Create a new GridPanel.
48095  * @param {Roo.grid.Grid} grid The grid for this panel
48096  * @param {String/Object} config A string to set only the panel's title, or a config object
48097  */
48098 Roo.GridPanel = function(grid, config){
48099     
48100   
48101     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
48102         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
48103         
48104     this.wrapper.dom.appendChild(grid.getGridEl().dom);
48105     
48106     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
48107     
48108     if(this.toolbar){
48109         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
48110     }
48111     // xtype created footer. - not sure if will work as we normally have to render first..
48112     if (this.footer && !this.footer.el && this.footer.xtype) {
48113         
48114         this.footer.container = this.grid.getView().getFooterPanel(true);
48115         this.footer.dataSource = this.grid.dataSource;
48116         this.footer = Roo.factory(this.footer, Roo);
48117         
48118     }
48119     
48120     grid.monitorWindowResize = false; // turn off autosizing
48121     grid.autoHeight = false;
48122     grid.autoWidth = false;
48123     this.grid = grid;
48124     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
48125 };
48126
48127 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
48128     getId : function(){
48129         return this.grid.id;
48130     },
48131     
48132     /**
48133      * Returns the grid for this panel
48134      * @return {Roo.grid.Grid} 
48135      */
48136     getGrid : function(){
48137         return this.grid;    
48138     },
48139     
48140     setSize : function(width, height){
48141         if(!this.ignoreResize(width, height)){
48142             var grid = this.grid;
48143             var size = this.adjustForComponents(width, height);
48144             grid.getGridEl().setSize(size.width, size.height);
48145             grid.autoSize();
48146         }
48147     },
48148     
48149     beforeSlide : function(){
48150         this.grid.getView().scroller.clip();
48151     },
48152     
48153     afterSlide : function(){
48154         this.grid.getView().scroller.unclip();
48155     },
48156     
48157     destroy : function(){
48158         this.grid.destroy();
48159         delete this.grid;
48160         Roo.GridPanel.superclass.destroy.call(this); 
48161     }
48162 });
48163
48164
48165 /**
48166  * @class Roo.NestedLayoutPanel
48167  * @extends Roo.ContentPanel
48168  * @constructor
48169  * Create a new NestedLayoutPanel.
48170  * 
48171  * 
48172  * @param {Roo.BorderLayout} layout The layout for this panel
48173  * @param {String/Object} config A string to set only the title or a config object
48174  */
48175 Roo.NestedLayoutPanel = function(layout, config)
48176 {
48177     // construct with only one argument..
48178     /* FIXME - implement nicer consturctors
48179     if (layout.layout) {
48180         config = layout;
48181         layout = config.layout;
48182         delete config.layout;
48183     }
48184     if (layout.xtype && !layout.getEl) {
48185         // then layout needs constructing..
48186         layout = Roo.factory(layout, Roo);
48187     }
48188     */
48189     
48190     
48191     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
48192     
48193     layout.monitorWindowResize = false; // turn off autosizing
48194     this.layout = layout;
48195     this.layout.getEl().addClass("x-layout-nested-layout");
48196     
48197     
48198     
48199     
48200 };
48201
48202 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
48203
48204     setSize : function(width, height){
48205         if(!this.ignoreResize(width, height)){
48206             var size = this.adjustForComponents(width, height);
48207             var el = this.layout.getEl();
48208             el.setSize(size.width, size.height);
48209             var touch = el.dom.offsetWidth;
48210             this.layout.layout();
48211             // ie requires a double layout on the first pass
48212             if(Roo.isIE && !this.initialized){
48213                 this.initialized = true;
48214                 this.layout.layout();
48215             }
48216         }
48217     },
48218     
48219     // activate all subpanels if not currently active..
48220     
48221     setActiveState : function(active){
48222         this.active = active;
48223         if(!active){
48224             this.fireEvent("deactivate", this);
48225             return;
48226         }
48227         
48228         this.fireEvent("activate", this);
48229         // not sure if this should happen before or after..
48230         if (!this.layout) {
48231             return; // should not happen..
48232         }
48233         var reg = false;
48234         for (var r in this.layout.regions) {
48235             reg = this.layout.getRegion(r);
48236             if (reg.getActivePanel()) {
48237                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
48238                 reg.setActivePanel(reg.getActivePanel());
48239                 continue;
48240             }
48241             if (!reg.panels.length) {
48242                 continue;
48243             }
48244             reg.showPanel(reg.getPanel(0));
48245         }
48246         
48247         
48248         
48249         
48250     },
48251     
48252     /**
48253      * Returns the nested BorderLayout for this panel
48254      * @return {Roo.BorderLayout} 
48255      */
48256     getLayout : function(){
48257         return this.layout;
48258     },
48259     
48260      /**
48261      * Adds a xtype elements to the layout of the nested panel
48262      * <pre><code>
48263
48264 panel.addxtype({
48265        xtype : 'ContentPanel',
48266        region: 'west',
48267        items: [ .... ]
48268    }
48269 );
48270
48271 panel.addxtype({
48272         xtype : 'NestedLayoutPanel',
48273         region: 'west',
48274         layout: {
48275            center: { },
48276            west: { }   
48277         },
48278         items : [ ... list of content panels or nested layout panels.. ]
48279    }
48280 );
48281 </code></pre>
48282      * @param {Object} cfg Xtype definition of item to add.
48283      */
48284     addxtype : function(cfg) {
48285         return this.layout.addxtype(cfg);
48286     
48287     }
48288 });
48289
48290 Roo.ScrollPanel = function(el, config, content){
48291     config = config || {};
48292     config.fitToFrame = true;
48293     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
48294     
48295     this.el.dom.style.overflow = "hidden";
48296     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
48297     this.el.removeClass("x-layout-inactive-content");
48298     this.el.on("mousewheel", this.onWheel, this);
48299
48300     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
48301     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
48302     up.unselectable(); down.unselectable();
48303     up.on("click", this.scrollUp, this);
48304     down.on("click", this.scrollDown, this);
48305     up.addClassOnOver("x-scroller-btn-over");
48306     down.addClassOnOver("x-scroller-btn-over");
48307     up.addClassOnClick("x-scroller-btn-click");
48308     down.addClassOnClick("x-scroller-btn-click");
48309     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
48310
48311     this.resizeEl = this.el;
48312     this.el = wrap; this.up = up; this.down = down;
48313 };
48314
48315 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
48316     increment : 100,
48317     wheelIncrement : 5,
48318     scrollUp : function(){
48319         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
48320     },
48321
48322     scrollDown : function(){
48323         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
48324     },
48325
48326     afterScroll : function(){
48327         var el = this.resizeEl;
48328         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
48329         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
48330         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
48331     },
48332
48333     setSize : function(){
48334         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
48335         this.afterScroll();
48336     },
48337
48338     onWheel : function(e){
48339         var d = e.getWheelDelta();
48340         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
48341         this.afterScroll();
48342         e.stopEvent();
48343     },
48344
48345     setContent : function(content, loadScripts){
48346         this.resizeEl.update(content, loadScripts);
48347     }
48348
48349 });
48350
48351
48352
48353
48354
48355
48356
48357
48358
48359 /**
48360  * @class Roo.TreePanel
48361  * @extends Roo.ContentPanel
48362  * @constructor
48363  * Create a new TreePanel. - defaults to fit/scoll contents.
48364  * @param {String/Object} config A string to set only the panel's title, or a config object
48365  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
48366  */
48367 Roo.TreePanel = function(config){
48368     var el = config.el;
48369     var tree = config.tree;
48370     delete config.tree; 
48371     delete config.el; // hopefull!
48372     
48373     // wrapper for IE7 strict & safari scroll issue
48374     
48375     var treeEl = el.createChild();
48376     config.resizeEl = treeEl;
48377     
48378     
48379     
48380     Roo.TreePanel.superclass.constructor.call(this, el, config);
48381  
48382  
48383     this.tree = new Roo.tree.TreePanel(treeEl , tree);
48384     //console.log(tree);
48385     this.on('activate', function()
48386     {
48387         if (this.tree.rendered) {
48388             return;
48389         }
48390         //console.log('render tree');
48391         this.tree.render();
48392     });
48393     // this should not be needed.. - it's actually the 'el' that resizes?
48394     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
48395     
48396     //this.on('resize',  function (cp, w, h) {
48397     //        this.tree.innerCt.setWidth(w);
48398     //        this.tree.innerCt.setHeight(h);
48399     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
48400     //});
48401
48402         
48403     
48404 };
48405
48406 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
48407     fitToFrame : true,
48408     autoScroll : true
48409 });
48410
48411
48412
48413
48414
48415
48416
48417
48418
48419
48420
48421 /*
48422  * Based on:
48423  * Ext JS Library 1.1.1
48424  * Copyright(c) 2006-2007, Ext JS, LLC.
48425  *
48426  * Originally Released Under LGPL - original licence link has changed is not relivant.
48427  *
48428  * Fork - LGPL
48429  * <script type="text/javascript">
48430  */
48431  
48432
48433 /**
48434  * @class Roo.ReaderLayout
48435  * @extends Roo.BorderLayout
48436  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
48437  * center region containing two nested regions (a top one for a list view and one for item preview below),
48438  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
48439  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
48440  * expedites the setup of the overall layout and regions for this common application style.
48441  * Example:
48442  <pre><code>
48443 var reader = new Roo.ReaderLayout();
48444 var CP = Roo.ContentPanel;  // shortcut for adding
48445
48446 reader.beginUpdate();
48447 reader.add("north", new CP("north", "North"));
48448 reader.add("west", new CP("west", {title: "West"}));
48449 reader.add("east", new CP("east", {title: "East"}));
48450
48451 reader.regions.listView.add(new CP("listView", "List"));
48452 reader.regions.preview.add(new CP("preview", "Preview"));
48453 reader.endUpdate();
48454 </code></pre>
48455 * @constructor
48456 * Create a new ReaderLayout
48457 * @param {Object} config Configuration options
48458 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
48459 * document.body if omitted)
48460 */
48461 Roo.ReaderLayout = function(config, renderTo){
48462     var c = config || {size:{}};
48463     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
48464         north: c.north !== false ? Roo.apply({
48465             split:false,
48466             initialSize: 32,
48467             titlebar: false
48468         }, c.north) : false,
48469         west: c.west !== false ? Roo.apply({
48470             split:true,
48471             initialSize: 200,
48472             minSize: 175,
48473             maxSize: 400,
48474             titlebar: true,
48475             collapsible: true,
48476             animate: true,
48477             margins:{left:5,right:0,bottom:5,top:5},
48478             cmargins:{left:5,right:5,bottom:5,top:5}
48479         }, c.west) : false,
48480         east: c.east !== false ? Roo.apply({
48481             split:true,
48482             initialSize: 200,
48483             minSize: 175,
48484             maxSize: 400,
48485             titlebar: true,
48486             collapsible: true,
48487             animate: true,
48488             margins:{left:0,right:5,bottom:5,top:5},
48489             cmargins:{left:5,right:5,bottom:5,top:5}
48490         }, c.east) : false,
48491         center: Roo.apply({
48492             tabPosition: 'top',
48493             autoScroll:false,
48494             closeOnTab: true,
48495             titlebar:false,
48496             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
48497         }, c.center)
48498     });
48499
48500     this.el.addClass('x-reader');
48501
48502     this.beginUpdate();
48503
48504     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
48505         south: c.preview !== false ? Roo.apply({
48506             split:true,
48507             initialSize: 200,
48508             minSize: 100,
48509             autoScroll:true,
48510             collapsible:true,
48511             titlebar: true,
48512             cmargins:{top:5,left:0, right:0, bottom:0}
48513         }, c.preview) : false,
48514         center: Roo.apply({
48515             autoScroll:false,
48516             titlebar:false,
48517             minHeight:200
48518         }, c.listView)
48519     });
48520     this.add('center', new Roo.NestedLayoutPanel(inner,
48521             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
48522
48523     this.endUpdate();
48524
48525     this.regions.preview = inner.getRegion('south');
48526     this.regions.listView = inner.getRegion('center');
48527 };
48528
48529 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
48530  * Based on:
48531  * Ext JS Library 1.1.1
48532  * Copyright(c) 2006-2007, Ext JS, LLC.
48533  *
48534  * Originally Released Under LGPL - original licence link has changed is not relivant.
48535  *
48536  * Fork - LGPL
48537  * <script type="text/javascript">
48538  */
48539  
48540 /**
48541  * @class Roo.grid.Grid
48542  * @extends Roo.util.Observable
48543  * This class represents the primary interface of a component based grid control.
48544  * <br><br>Usage:<pre><code>
48545  var grid = new Roo.grid.Grid("my-container-id", {
48546      ds: myDataStore,
48547      cm: myColModel,
48548      selModel: mySelectionModel,
48549      autoSizeColumns: true,
48550      monitorWindowResize: false,
48551      trackMouseOver: true
48552  });
48553  // set any options
48554  grid.render();
48555  * </code></pre>
48556  * <b>Common Problems:</b><br/>
48557  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
48558  * element will correct this<br/>
48559  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
48560  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
48561  * are unpredictable.<br/>
48562  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
48563  * grid to calculate dimensions/offsets.<br/>
48564   * @constructor
48565  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
48566  * The container MUST have some type of size defined for the grid to fill. The container will be
48567  * automatically set to position relative if it isn't already.
48568  * @param {Object} config A config object that sets properties on this grid.
48569  */
48570 Roo.grid.Grid = function(container, config){
48571         // initialize the container
48572         this.container = Roo.get(container);
48573         this.container.update("");
48574         this.container.setStyle("overflow", "hidden");
48575     this.container.addClass('x-grid-container');
48576
48577     this.id = this.container.id;
48578
48579     Roo.apply(this, config);
48580     // check and correct shorthanded configs
48581     if(this.ds){
48582         this.dataSource = this.ds;
48583         delete this.ds;
48584     }
48585     if(this.cm){
48586         this.colModel = this.cm;
48587         delete this.cm;
48588     }
48589     if(this.sm){
48590         this.selModel = this.sm;
48591         delete this.sm;
48592     }
48593
48594     if (this.selModel) {
48595         this.selModel = Roo.factory(this.selModel, Roo.grid);
48596         this.sm = this.selModel;
48597         this.sm.xmodule = this.xmodule || false;
48598     }
48599     if (typeof(this.colModel.config) == 'undefined') {
48600         this.colModel = new Roo.grid.ColumnModel(this.colModel);
48601         this.cm = this.colModel;
48602         this.cm.xmodule = this.xmodule || false;
48603     }
48604     if (this.dataSource) {
48605         this.dataSource= Roo.factory(this.dataSource, Roo.data);
48606         this.ds = this.dataSource;
48607         this.ds.xmodule = this.xmodule || false;
48608          
48609     }
48610     
48611     
48612     
48613     if(this.width){
48614         this.container.setWidth(this.width);
48615     }
48616
48617     if(this.height){
48618         this.container.setHeight(this.height);
48619     }
48620     /** @private */
48621         this.addEvents({
48622         // raw events
48623         /**
48624          * @event click
48625          * The raw click event for the entire grid.
48626          * @param {Roo.EventObject} e
48627          */
48628         "click" : true,
48629         /**
48630          * @event dblclick
48631          * The raw dblclick event for the entire grid.
48632          * @param {Roo.EventObject} e
48633          */
48634         "dblclick" : true,
48635         /**
48636          * @event contextmenu
48637          * The raw contextmenu event for the entire grid.
48638          * @param {Roo.EventObject} e
48639          */
48640         "contextmenu" : true,
48641         /**
48642          * @event mousedown
48643          * The raw mousedown event for the entire grid.
48644          * @param {Roo.EventObject} e
48645          */
48646         "mousedown" : true,
48647         /**
48648          * @event mouseup
48649          * The raw mouseup event for the entire grid.
48650          * @param {Roo.EventObject} e
48651          */
48652         "mouseup" : true,
48653         /**
48654          * @event mouseover
48655          * The raw mouseover event for the entire grid.
48656          * @param {Roo.EventObject} e
48657          */
48658         "mouseover" : true,
48659         /**
48660          * @event mouseout
48661          * The raw mouseout event for the entire grid.
48662          * @param {Roo.EventObject} e
48663          */
48664         "mouseout" : true,
48665         /**
48666          * @event keypress
48667          * The raw keypress event for the entire grid.
48668          * @param {Roo.EventObject} e
48669          */
48670         "keypress" : true,
48671         /**
48672          * @event keydown
48673          * The raw keydown event for the entire grid.
48674          * @param {Roo.EventObject} e
48675          */
48676         "keydown" : true,
48677
48678         // custom events
48679
48680         /**
48681          * @event cellclick
48682          * Fires when a cell is clicked
48683          * @param {Grid} this
48684          * @param {Number} rowIndex
48685          * @param {Number} columnIndex
48686          * @param {Roo.EventObject} e
48687          */
48688         "cellclick" : true,
48689         /**
48690          * @event celldblclick
48691          * Fires when a cell is double clicked
48692          * @param {Grid} this
48693          * @param {Number} rowIndex
48694          * @param {Number} columnIndex
48695          * @param {Roo.EventObject} e
48696          */
48697         "celldblclick" : true,
48698         /**
48699          * @event rowclick
48700          * Fires when a row is clicked
48701          * @param {Grid} this
48702          * @param {Number} rowIndex
48703          * @param {Roo.EventObject} e
48704          */
48705         "rowclick" : true,
48706         /**
48707          * @event rowdblclick
48708          * Fires when a row is double clicked
48709          * @param {Grid} this
48710          * @param {Number} rowIndex
48711          * @param {Roo.EventObject} e
48712          */
48713         "rowdblclick" : true,
48714         /**
48715          * @event headerclick
48716          * Fires when a header is clicked
48717          * @param {Grid} this
48718          * @param {Number} columnIndex
48719          * @param {Roo.EventObject} e
48720          */
48721         "headerclick" : true,
48722         /**
48723          * @event headerdblclick
48724          * Fires when a header cell is double clicked
48725          * @param {Grid} this
48726          * @param {Number} columnIndex
48727          * @param {Roo.EventObject} e
48728          */
48729         "headerdblclick" : true,
48730         /**
48731          * @event rowcontextmenu
48732          * Fires when a row is right clicked
48733          * @param {Grid} this
48734          * @param {Number} rowIndex
48735          * @param {Roo.EventObject} e
48736          */
48737         "rowcontextmenu" : true,
48738         /**
48739          * @event cellcontextmenu
48740          * Fires when a cell is right clicked
48741          * @param {Grid} this
48742          * @param {Number} rowIndex
48743          * @param {Number} cellIndex
48744          * @param {Roo.EventObject} e
48745          */
48746          "cellcontextmenu" : true,
48747         /**
48748          * @event headercontextmenu
48749          * Fires when a header is right clicked
48750          * @param {Grid} this
48751          * @param {Number} columnIndex
48752          * @param {Roo.EventObject} e
48753          */
48754         "headercontextmenu" : true,
48755         /**
48756          * @event bodyscroll
48757          * Fires when the body element is scrolled
48758          * @param {Number} scrollLeft
48759          * @param {Number} scrollTop
48760          */
48761         "bodyscroll" : true,
48762         /**
48763          * @event columnresize
48764          * Fires when the user resizes a column
48765          * @param {Number} columnIndex
48766          * @param {Number} newSize
48767          */
48768         "columnresize" : true,
48769         /**
48770          * @event columnmove
48771          * Fires when the user moves a column
48772          * @param {Number} oldIndex
48773          * @param {Number} newIndex
48774          */
48775         "columnmove" : true,
48776         /**
48777          * @event startdrag
48778          * Fires when row(s) start being dragged
48779          * @param {Grid} this
48780          * @param {Roo.GridDD} dd The drag drop object
48781          * @param {event} e The raw browser event
48782          */
48783         "startdrag" : true,
48784         /**
48785          * @event enddrag
48786          * Fires when a drag operation is complete
48787          * @param {Grid} this
48788          * @param {Roo.GridDD} dd The drag drop object
48789          * @param {event} e The raw browser event
48790          */
48791         "enddrag" : true,
48792         /**
48793          * @event dragdrop
48794          * Fires when dragged row(s) are dropped on a valid DD target
48795          * @param {Grid} this
48796          * @param {Roo.GridDD} dd The drag drop object
48797          * @param {String} targetId The target drag drop object
48798          * @param {event} e The raw browser event
48799          */
48800         "dragdrop" : true,
48801         /**
48802          * @event dragover
48803          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
48804          * @param {Grid} this
48805          * @param {Roo.GridDD} dd The drag drop object
48806          * @param {String} targetId The target drag drop object
48807          * @param {event} e The raw browser event
48808          */
48809         "dragover" : true,
48810         /**
48811          * @event dragenter
48812          *  Fires when the dragged row(s) first cross another DD target while being dragged
48813          * @param {Grid} this
48814          * @param {Roo.GridDD} dd The drag drop object
48815          * @param {String} targetId The target drag drop object
48816          * @param {event} e The raw browser event
48817          */
48818         "dragenter" : true,
48819         /**
48820          * @event dragout
48821          * Fires when the dragged row(s) leave another DD target while being dragged
48822          * @param {Grid} this
48823          * @param {Roo.GridDD} dd The drag drop object
48824          * @param {String} targetId The target drag drop object
48825          * @param {event} e The raw browser event
48826          */
48827         "dragout" : true,
48828         /**
48829          * @event rowclass
48830          * Fires when a row is rendered, so you can change add a style to it.
48831          * @param {GridView} gridview   The grid view
48832          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
48833          */
48834         'rowclass' : true,
48835
48836         /**
48837          * @event render
48838          * Fires when the grid is rendered
48839          * @param {Grid} grid
48840          */
48841         'render' : true
48842     });
48843
48844     Roo.grid.Grid.superclass.constructor.call(this);
48845 };
48846 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
48847     
48848     /**
48849      * @cfg {String} ddGroup - drag drop group.
48850      */
48851
48852     /**
48853      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
48854      */
48855     minColumnWidth : 25,
48856
48857     /**
48858      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
48859      * <b>on initial render.</b> It is more efficient to explicitly size the columns
48860      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
48861      */
48862     autoSizeColumns : false,
48863
48864     /**
48865      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
48866      */
48867     autoSizeHeaders : true,
48868
48869     /**
48870      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
48871      */
48872     monitorWindowResize : true,
48873
48874     /**
48875      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
48876      * rows measured to get a columns size. Default is 0 (all rows).
48877      */
48878     maxRowsToMeasure : 0,
48879
48880     /**
48881      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
48882      */
48883     trackMouseOver : true,
48884
48885     /**
48886     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
48887     */
48888     
48889     /**
48890     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
48891     */
48892     enableDragDrop : false,
48893     
48894     /**
48895     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
48896     */
48897     enableColumnMove : true,
48898     
48899     /**
48900     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
48901     */
48902     enableColumnHide : true,
48903     
48904     /**
48905     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
48906     */
48907     enableRowHeightSync : false,
48908     
48909     /**
48910     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
48911     */
48912     stripeRows : true,
48913     
48914     /**
48915     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
48916     */
48917     autoHeight : false,
48918
48919     /**
48920      * @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.
48921      */
48922     autoExpandColumn : false,
48923
48924     /**
48925     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
48926     * Default is 50.
48927     */
48928     autoExpandMin : 50,
48929
48930     /**
48931     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
48932     */
48933     autoExpandMax : 1000,
48934
48935     /**
48936     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
48937     */
48938     view : null,
48939
48940     /**
48941     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
48942     */
48943     loadMask : false,
48944     /**
48945     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
48946     */
48947     dropTarget: false,
48948     
48949    
48950     
48951     // private
48952     rendered : false,
48953
48954     /**
48955     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
48956     * of a fixed width. Default is false.
48957     */
48958     /**
48959     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
48960     */
48961     /**
48962      * Called once after all setup has been completed and the grid is ready to be rendered.
48963      * @return {Roo.grid.Grid} this
48964      */
48965     render : function()
48966     {
48967         var c = this.container;
48968         // try to detect autoHeight/width mode
48969         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
48970             this.autoHeight = true;
48971         }
48972         var view = this.getView();
48973         view.init(this);
48974
48975         c.on("click", this.onClick, this);
48976         c.on("dblclick", this.onDblClick, this);
48977         c.on("contextmenu", this.onContextMenu, this);
48978         c.on("keydown", this.onKeyDown, this);
48979
48980         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
48981
48982         this.getSelectionModel().init(this);
48983
48984         view.render();
48985
48986         if(this.loadMask){
48987             this.loadMask = new Roo.LoadMask(this.container,
48988                     Roo.apply({store:this.dataSource}, this.loadMask));
48989         }
48990         
48991         
48992         if (this.toolbar && this.toolbar.xtype) {
48993             this.toolbar.container = this.getView().getHeaderPanel(true);
48994             this.toolbar = new Roo.Toolbar(this.toolbar);
48995         }
48996         if (this.footer && this.footer.xtype) {
48997             this.footer.dataSource = this.getDataSource();
48998             this.footer.container = this.getView().getFooterPanel(true);
48999             this.footer = Roo.factory(this.footer, Roo);
49000         }
49001         if (this.dropTarget && this.dropTarget.xtype) {
49002             delete this.dropTarget.xtype;
49003             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
49004         }
49005         
49006         
49007         this.rendered = true;
49008         this.fireEvent('render', this);
49009         return this;
49010     },
49011
49012         /**
49013          * Reconfigures the grid to use a different Store and Column Model.
49014          * The View will be bound to the new objects and refreshed.
49015          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
49016          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
49017          */
49018     reconfigure : function(dataSource, colModel){
49019         if(this.loadMask){
49020             this.loadMask.destroy();
49021             this.loadMask = new Roo.LoadMask(this.container,
49022                     Roo.apply({store:dataSource}, this.loadMask));
49023         }
49024         this.view.bind(dataSource, colModel);
49025         this.dataSource = dataSource;
49026         this.colModel = colModel;
49027         this.view.refresh(true);
49028     },
49029
49030     // private
49031     onKeyDown : function(e){
49032         this.fireEvent("keydown", e);
49033     },
49034
49035     /**
49036      * Destroy this grid.
49037      * @param {Boolean} removeEl True to remove the element
49038      */
49039     destroy : function(removeEl, keepListeners){
49040         if(this.loadMask){
49041             this.loadMask.destroy();
49042         }
49043         var c = this.container;
49044         c.removeAllListeners();
49045         this.view.destroy();
49046         this.colModel.purgeListeners();
49047         if(!keepListeners){
49048             this.purgeListeners();
49049         }
49050         c.update("");
49051         if(removeEl === true){
49052             c.remove();
49053         }
49054     },
49055
49056     // private
49057     processEvent : function(name, e){
49058         this.fireEvent(name, e);
49059         var t = e.getTarget();
49060         var v = this.view;
49061         var header = v.findHeaderIndex(t);
49062         if(header !== false){
49063             this.fireEvent("header" + name, this, header, e);
49064         }else{
49065             var row = v.findRowIndex(t);
49066             var cell = v.findCellIndex(t);
49067             if(row !== false){
49068                 this.fireEvent("row" + name, this, row, e);
49069                 if(cell !== false){
49070                     this.fireEvent("cell" + name, this, row, cell, e);
49071                 }
49072             }
49073         }
49074     },
49075
49076     // private
49077     onClick : function(e){
49078         this.processEvent("click", e);
49079     },
49080
49081     // private
49082     onContextMenu : function(e, t){
49083         this.processEvent("contextmenu", e);
49084     },
49085
49086     // private
49087     onDblClick : function(e){
49088         this.processEvent("dblclick", e);
49089     },
49090
49091     // private
49092     walkCells : function(row, col, step, fn, scope){
49093         var cm = this.colModel, clen = cm.getColumnCount();
49094         var ds = this.dataSource, rlen = ds.getCount(), first = true;
49095         if(step < 0){
49096             if(col < 0){
49097                 row--;
49098                 first = false;
49099             }
49100             while(row >= 0){
49101                 if(!first){
49102                     col = clen-1;
49103                 }
49104                 first = false;
49105                 while(col >= 0){
49106                     if(fn.call(scope || this, row, col, cm) === true){
49107                         return [row, col];
49108                     }
49109                     col--;
49110                 }
49111                 row--;
49112             }
49113         } else {
49114             if(col >= clen){
49115                 row++;
49116                 first = false;
49117             }
49118             while(row < rlen){
49119                 if(!first){
49120                     col = 0;
49121                 }
49122                 first = false;
49123                 while(col < clen){
49124                     if(fn.call(scope || this, row, col, cm) === true){
49125                         return [row, col];
49126                     }
49127                     col++;
49128                 }
49129                 row++;
49130             }
49131         }
49132         return null;
49133     },
49134
49135     // private
49136     getSelections : function(){
49137         return this.selModel.getSelections();
49138     },
49139
49140     /**
49141      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
49142      * but if manual update is required this method will initiate it.
49143      */
49144     autoSize : function(){
49145         if(this.rendered){
49146             this.view.layout();
49147             if(this.view.adjustForScroll){
49148                 this.view.adjustForScroll();
49149             }
49150         }
49151     },
49152
49153     /**
49154      * Returns the grid's underlying element.
49155      * @return {Element} The element
49156      */
49157     getGridEl : function(){
49158         return this.container;
49159     },
49160
49161     // private for compatibility, overridden by editor grid
49162     stopEditing : function(){},
49163
49164     /**
49165      * Returns the grid's SelectionModel.
49166      * @return {SelectionModel}
49167      */
49168     getSelectionModel : function(){
49169         if(!this.selModel){
49170             this.selModel = new Roo.grid.RowSelectionModel();
49171         }
49172         return this.selModel;
49173     },
49174
49175     /**
49176      * Returns the grid's DataSource.
49177      * @return {DataSource}
49178      */
49179     getDataSource : function(){
49180         return this.dataSource;
49181     },
49182
49183     /**
49184      * Returns the grid's ColumnModel.
49185      * @return {ColumnModel}
49186      */
49187     getColumnModel : function(){
49188         return this.colModel;
49189     },
49190
49191     /**
49192      * Returns the grid's GridView object.
49193      * @return {GridView}
49194      */
49195     getView : function(){
49196         if(!this.view){
49197             this.view = new Roo.grid.GridView(this.viewConfig);
49198         }
49199         return this.view;
49200     },
49201     /**
49202      * Called to get grid's drag proxy text, by default returns this.ddText.
49203      * @return {String}
49204      */
49205     getDragDropText : function(){
49206         var count = this.selModel.getCount();
49207         return String.format(this.ddText, count, count == 1 ? '' : 's');
49208     }
49209 });
49210 /**
49211  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
49212  * %0 is replaced with the number of selected rows.
49213  * @type String
49214  */
49215 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
49216  * Based on:
49217  * Ext JS Library 1.1.1
49218  * Copyright(c) 2006-2007, Ext JS, LLC.
49219  *
49220  * Originally Released Under LGPL - original licence link has changed is not relivant.
49221  *
49222  * Fork - LGPL
49223  * <script type="text/javascript">
49224  */
49225  
49226 Roo.grid.AbstractGridView = function(){
49227         this.grid = null;
49228         
49229         this.events = {
49230             "beforerowremoved" : true,
49231             "beforerowsinserted" : true,
49232             "beforerefresh" : true,
49233             "rowremoved" : true,
49234             "rowsinserted" : true,
49235             "rowupdated" : true,
49236             "refresh" : true
49237         };
49238     Roo.grid.AbstractGridView.superclass.constructor.call(this);
49239 };
49240
49241 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
49242     rowClass : "x-grid-row",
49243     cellClass : "x-grid-cell",
49244     tdClass : "x-grid-td",
49245     hdClass : "x-grid-hd",
49246     splitClass : "x-grid-hd-split",
49247     
49248         init: function(grid){
49249         this.grid = grid;
49250                 var cid = this.grid.getGridEl().id;
49251         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
49252         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
49253         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
49254         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
49255         },
49256         
49257         getColumnRenderers : function(){
49258         var renderers = [];
49259         var cm = this.grid.colModel;
49260         var colCount = cm.getColumnCount();
49261         for(var i = 0; i < colCount; i++){
49262             renderers[i] = cm.getRenderer(i);
49263         }
49264         return renderers;
49265     },
49266     
49267     getColumnIds : function(){
49268         var ids = [];
49269         var cm = this.grid.colModel;
49270         var colCount = cm.getColumnCount();
49271         for(var i = 0; i < colCount; i++){
49272             ids[i] = cm.getColumnId(i);
49273         }
49274         return ids;
49275     },
49276     
49277     getDataIndexes : function(){
49278         if(!this.indexMap){
49279             this.indexMap = this.buildIndexMap();
49280         }
49281         return this.indexMap.colToData;
49282     },
49283     
49284     getColumnIndexByDataIndex : function(dataIndex){
49285         if(!this.indexMap){
49286             this.indexMap = this.buildIndexMap();
49287         }
49288         return this.indexMap.dataToCol[dataIndex];
49289     },
49290     
49291     /**
49292      * Set a css style for a column dynamically. 
49293      * @param {Number} colIndex The index of the column
49294      * @param {String} name The css property name
49295      * @param {String} value The css value
49296      */
49297     setCSSStyle : function(colIndex, name, value){
49298         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
49299         Roo.util.CSS.updateRule(selector, name, value);
49300     },
49301     
49302     generateRules : function(cm){
49303         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
49304         Roo.util.CSS.removeStyleSheet(rulesId);
49305         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49306             var cid = cm.getColumnId(i);
49307             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
49308                          this.tdSelector, cid, " {\n}\n",
49309                          this.hdSelector, cid, " {\n}\n",
49310                          this.splitSelector, cid, " {\n}\n");
49311         }
49312         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49313     }
49314 });/*
49315  * Based on:
49316  * Ext JS Library 1.1.1
49317  * Copyright(c) 2006-2007, Ext JS, LLC.
49318  *
49319  * Originally Released Under LGPL - original licence link has changed is not relivant.
49320  *
49321  * Fork - LGPL
49322  * <script type="text/javascript">
49323  */
49324
49325 // private
49326 // This is a support class used internally by the Grid components
49327 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
49328     this.grid = grid;
49329     this.view = grid.getView();
49330     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
49331     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
49332     if(hd2){
49333         this.setHandleElId(Roo.id(hd));
49334         this.setOuterHandleElId(Roo.id(hd2));
49335     }
49336     this.scroll = false;
49337 };
49338 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
49339     maxDragWidth: 120,
49340     getDragData : function(e){
49341         var t = Roo.lib.Event.getTarget(e);
49342         var h = this.view.findHeaderCell(t);
49343         if(h){
49344             return {ddel: h.firstChild, header:h};
49345         }
49346         return false;
49347     },
49348
49349     onInitDrag : function(e){
49350         this.view.headersDisabled = true;
49351         var clone = this.dragData.ddel.cloneNode(true);
49352         clone.id = Roo.id();
49353         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
49354         this.proxy.update(clone);
49355         return true;
49356     },
49357
49358     afterValidDrop : function(){
49359         var v = this.view;
49360         setTimeout(function(){
49361             v.headersDisabled = false;
49362         }, 50);
49363     },
49364
49365     afterInvalidDrop : function(){
49366         var v = this.view;
49367         setTimeout(function(){
49368             v.headersDisabled = false;
49369         }, 50);
49370     }
49371 });
49372 /*
49373  * Based on:
49374  * Ext JS Library 1.1.1
49375  * Copyright(c) 2006-2007, Ext JS, LLC.
49376  *
49377  * Originally Released Under LGPL - original licence link has changed is not relivant.
49378  *
49379  * Fork - LGPL
49380  * <script type="text/javascript">
49381  */
49382 // private
49383 // This is a support class used internally by the Grid components
49384 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
49385     this.grid = grid;
49386     this.view = grid.getView();
49387     // split the proxies so they don't interfere with mouse events
49388     this.proxyTop = Roo.DomHelper.append(document.body, {
49389         cls:"col-move-top", html:"&#160;"
49390     }, true);
49391     this.proxyBottom = Roo.DomHelper.append(document.body, {
49392         cls:"col-move-bottom", html:"&#160;"
49393     }, true);
49394     this.proxyTop.hide = this.proxyBottom.hide = function(){
49395         this.setLeftTop(-100,-100);
49396         this.setStyle("visibility", "hidden");
49397     };
49398     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
49399     // temporarily disabled
49400     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
49401     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
49402 };
49403 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
49404     proxyOffsets : [-4, -9],
49405     fly: Roo.Element.fly,
49406
49407     getTargetFromEvent : function(e){
49408         var t = Roo.lib.Event.getTarget(e);
49409         var cindex = this.view.findCellIndex(t);
49410         if(cindex !== false){
49411             return this.view.getHeaderCell(cindex);
49412         }
49413         return null;
49414     },
49415
49416     nextVisible : function(h){
49417         var v = this.view, cm = this.grid.colModel;
49418         h = h.nextSibling;
49419         while(h){
49420             if(!cm.isHidden(v.getCellIndex(h))){
49421                 return h;
49422             }
49423             h = h.nextSibling;
49424         }
49425         return null;
49426     },
49427
49428     prevVisible : function(h){
49429         var v = this.view, cm = this.grid.colModel;
49430         h = h.prevSibling;
49431         while(h){
49432             if(!cm.isHidden(v.getCellIndex(h))){
49433                 return h;
49434             }
49435             h = h.prevSibling;
49436         }
49437         return null;
49438     },
49439
49440     positionIndicator : function(h, n, e){
49441         var x = Roo.lib.Event.getPageX(e);
49442         var r = Roo.lib.Dom.getRegion(n.firstChild);
49443         var px, pt, py = r.top + this.proxyOffsets[1];
49444         if((r.right - x) <= (r.right-r.left)/2){
49445             px = r.right+this.view.borderWidth;
49446             pt = "after";
49447         }else{
49448             px = r.left;
49449             pt = "before";
49450         }
49451         var oldIndex = this.view.getCellIndex(h);
49452         var newIndex = this.view.getCellIndex(n);
49453
49454         if(this.grid.colModel.isFixed(newIndex)){
49455             return false;
49456         }
49457
49458         var locked = this.grid.colModel.isLocked(newIndex);
49459
49460         if(pt == "after"){
49461             newIndex++;
49462         }
49463         if(oldIndex < newIndex){
49464             newIndex--;
49465         }
49466         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
49467             return false;
49468         }
49469         px +=  this.proxyOffsets[0];
49470         this.proxyTop.setLeftTop(px, py);
49471         this.proxyTop.show();
49472         if(!this.bottomOffset){
49473             this.bottomOffset = this.view.mainHd.getHeight();
49474         }
49475         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
49476         this.proxyBottom.show();
49477         return pt;
49478     },
49479
49480     onNodeEnter : function(n, dd, e, data){
49481         if(data.header != n){
49482             this.positionIndicator(data.header, n, e);
49483         }
49484     },
49485
49486     onNodeOver : function(n, dd, e, data){
49487         var result = false;
49488         if(data.header != n){
49489             result = this.positionIndicator(data.header, n, e);
49490         }
49491         if(!result){
49492             this.proxyTop.hide();
49493             this.proxyBottom.hide();
49494         }
49495         return result ? this.dropAllowed : this.dropNotAllowed;
49496     },
49497
49498     onNodeOut : function(n, dd, e, data){
49499         this.proxyTop.hide();
49500         this.proxyBottom.hide();
49501     },
49502
49503     onNodeDrop : function(n, dd, e, data){
49504         var h = data.header;
49505         if(h != n){
49506             var cm = this.grid.colModel;
49507             var x = Roo.lib.Event.getPageX(e);
49508             var r = Roo.lib.Dom.getRegion(n.firstChild);
49509             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
49510             var oldIndex = this.view.getCellIndex(h);
49511             var newIndex = this.view.getCellIndex(n);
49512             var locked = cm.isLocked(newIndex);
49513             if(pt == "after"){
49514                 newIndex++;
49515             }
49516             if(oldIndex < newIndex){
49517                 newIndex--;
49518             }
49519             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
49520                 return false;
49521             }
49522             cm.setLocked(oldIndex, locked, true);
49523             cm.moveColumn(oldIndex, newIndex);
49524             this.grid.fireEvent("columnmove", oldIndex, newIndex);
49525             return true;
49526         }
49527         return false;
49528     }
49529 });
49530 /*
49531  * Based on:
49532  * Ext JS Library 1.1.1
49533  * Copyright(c) 2006-2007, Ext JS, LLC.
49534  *
49535  * Originally Released Under LGPL - original licence link has changed is not relivant.
49536  *
49537  * Fork - LGPL
49538  * <script type="text/javascript">
49539  */
49540   
49541 /**
49542  * @class Roo.grid.GridView
49543  * @extends Roo.util.Observable
49544  *
49545  * @constructor
49546  * @param {Object} config
49547  */
49548 Roo.grid.GridView = function(config){
49549     Roo.grid.GridView.superclass.constructor.call(this);
49550     this.el = null;
49551
49552     Roo.apply(this, config);
49553 };
49554
49555 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
49556
49557     
49558     rowClass : "x-grid-row",
49559
49560     cellClass : "x-grid-col",
49561
49562     tdClass : "x-grid-td",
49563
49564     hdClass : "x-grid-hd",
49565
49566     splitClass : "x-grid-split",
49567
49568     sortClasses : ["sort-asc", "sort-desc"],
49569
49570     enableMoveAnim : false,
49571
49572     hlColor: "C3DAF9",
49573
49574     dh : Roo.DomHelper,
49575
49576     fly : Roo.Element.fly,
49577
49578     css : Roo.util.CSS,
49579
49580     borderWidth: 1,
49581
49582     splitOffset: 3,
49583
49584     scrollIncrement : 22,
49585
49586     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
49587
49588     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
49589
49590     bind : function(ds, cm){
49591         if(this.ds){
49592             this.ds.un("load", this.onLoad, this);
49593             this.ds.un("datachanged", this.onDataChange, this);
49594             this.ds.un("add", this.onAdd, this);
49595             this.ds.un("remove", this.onRemove, this);
49596             this.ds.un("update", this.onUpdate, this);
49597             this.ds.un("clear", this.onClear, this);
49598         }
49599         if(ds){
49600             ds.on("load", this.onLoad, this);
49601             ds.on("datachanged", this.onDataChange, this);
49602             ds.on("add", this.onAdd, this);
49603             ds.on("remove", this.onRemove, this);
49604             ds.on("update", this.onUpdate, this);
49605             ds.on("clear", this.onClear, this);
49606         }
49607         this.ds = ds;
49608
49609         if(this.cm){
49610             this.cm.un("widthchange", this.onColWidthChange, this);
49611             this.cm.un("headerchange", this.onHeaderChange, this);
49612             this.cm.un("hiddenchange", this.onHiddenChange, this);
49613             this.cm.un("columnmoved", this.onColumnMove, this);
49614             this.cm.un("columnlockchange", this.onColumnLock, this);
49615         }
49616         if(cm){
49617             this.generateRules(cm);
49618             cm.on("widthchange", this.onColWidthChange, this);
49619             cm.on("headerchange", this.onHeaderChange, this);
49620             cm.on("hiddenchange", this.onHiddenChange, this);
49621             cm.on("columnmoved", this.onColumnMove, this);
49622             cm.on("columnlockchange", this.onColumnLock, this);
49623         }
49624         this.cm = cm;
49625     },
49626
49627     init: function(grid){
49628         Roo.grid.GridView.superclass.init.call(this, grid);
49629
49630         this.bind(grid.dataSource, grid.colModel);
49631
49632         grid.on("headerclick", this.handleHeaderClick, this);
49633
49634         if(grid.trackMouseOver){
49635             grid.on("mouseover", this.onRowOver, this);
49636             grid.on("mouseout", this.onRowOut, this);
49637         }
49638         grid.cancelTextSelection = function(){};
49639         this.gridId = grid.id;
49640
49641         var tpls = this.templates || {};
49642
49643         if(!tpls.master){
49644             tpls.master = new Roo.Template(
49645                '<div class="x-grid" hidefocus="true">',
49646                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
49647                   '<div class="x-grid-topbar"></div>',
49648                   '<div class="x-grid-scroller"><div></div></div>',
49649                   '<div class="x-grid-locked">',
49650                       '<div class="x-grid-header">{lockedHeader}</div>',
49651                       '<div class="x-grid-body">{lockedBody}</div>',
49652                   "</div>",
49653                   '<div class="x-grid-viewport">',
49654                       '<div class="x-grid-header">{header}</div>',
49655                       '<div class="x-grid-body">{body}</div>',
49656                   "</div>",
49657                   '<div class="x-grid-bottombar"></div>',
49658                  
49659                   '<div class="x-grid-resize-proxy">&#160;</div>',
49660                "</div>"
49661             );
49662             tpls.master.disableformats = true;
49663         }
49664
49665         if(!tpls.header){
49666             tpls.header = new Roo.Template(
49667                '<table border="0" cellspacing="0" cellpadding="0">',
49668                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
49669                "</table>{splits}"
49670             );
49671             tpls.header.disableformats = true;
49672         }
49673         tpls.header.compile();
49674
49675         if(!tpls.hcell){
49676             tpls.hcell = new Roo.Template(
49677                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
49678                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
49679                 "</div></td>"
49680              );
49681              tpls.hcell.disableFormats = true;
49682         }
49683         tpls.hcell.compile();
49684
49685         if(!tpls.hsplit){
49686             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
49687             tpls.hsplit.disableFormats = true;
49688         }
49689         tpls.hsplit.compile();
49690
49691         if(!tpls.body){
49692             tpls.body = new Roo.Template(
49693                '<table border="0" cellspacing="0" cellpadding="0">',
49694                "<tbody>{rows}</tbody>",
49695                "</table>"
49696             );
49697             tpls.body.disableFormats = true;
49698         }
49699         tpls.body.compile();
49700
49701         if(!tpls.row){
49702             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
49703             tpls.row.disableFormats = true;
49704         }
49705         tpls.row.compile();
49706
49707         if(!tpls.cell){
49708             tpls.cell = new Roo.Template(
49709                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
49710                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
49711                 "</td>"
49712             );
49713             tpls.cell.disableFormats = true;
49714         }
49715         tpls.cell.compile();
49716
49717         this.templates = tpls;
49718     },
49719
49720     // remap these for backwards compat
49721     onColWidthChange : function(){
49722         this.updateColumns.apply(this, arguments);
49723     },
49724     onHeaderChange : function(){
49725         this.updateHeaders.apply(this, arguments);
49726     }, 
49727     onHiddenChange : function(){
49728         this.handleHiddenChange.apply(this, arguments);
49729     },
49730     onColumnMove : function(){
49731         this.handleColumnMove.apply(this, arguments);
49732     },
49733     onColumnLock : function(){
49734         this.handleLockChange.apply(this, arguments);
49735     },
49736
49737     onDataChange : function(){
49738         this.refresh();
49739         this.updateHeaderSortState();
49740     },
49741
49742     onClear : function(){
49743         this.refresh();
49744     },
49745
49746     onUpdate : function(ds, record){
49747         this.refreshRow(record);
49748     },
49749
49750     refreshRow : function(record){
49751         var ds = this.ds, index;
49752         if(typeof record == 'number'){
49753             index = record;
49754             record = ds.getAt(index);
49755         }else{
49756             index = ds.indexOf(record);
49757         }
49758         this.insertRows(ds, index, index, true);
49759         this.onRemove(ds, record, index+1, true);
49760         this.syncRowHeights(index, index);
49761         this.layout();
49762         this.fireEvent("rowupdated", this, index, record);
49763     },
49764
49765     onAdd : function(ds, records, index){
49766         this.insertRows(ds, index, index + (records.length-1));
49767     },
49768
49769     onRemove : function(ds, record, index, isUpdate){
49770         if(isUpdate !== true){
49771             this.fireEvent("beforerowremoved", this, index, record);
49772         }
49773         var bt = this.getBodyTable(), lt = this.getLockedTable();
49774         if(bt.rows[index]){
49775             bt.firstChild.removeChild(bt.rows[index]);
49776         }
49777         if(lt.rows[index]){
49778             lt.firstChild.removeChild(lt.rows[index]);
49779         }
49780         if(isUpdate !== true){
49781             this.stripeRows(index);
49782             this.syncRowHeights(index, index);
49783             this.layout();
49784             this.fireEvent("rowremoved", this, index, record);
49785         }
49786     },
49787
49788     onLoad : function(){
49789         this.scrollToTop();
49790     },
49791
49792     /**
49793      * Scrolls the grid to the top
49794      */
49795     scrollToTop : function(){
49796         if(this.scroller){
49797             this.scroller.dom.scrollTop = 0;
49798             this.syncScroll();
49799         }
49800     },
49801
49802     /**
49803      * Gets a panel in the header of the grid that can be used for toolbars etc.
49804      * After modifying the contents of this panel a call to grid.autoSize() may be
49805      * required to register any changes in size.
49806      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
49807      * @return Roo.Element
49808      */
49809     getHeaderPanel : function(doShow){
49810         if(doShow){
49811             this.headerPanel.show();
49812         }
49813         return this.headerPanel;
49814     },
49815
49816     /**
49817      * Gets a panel in the footer of the grid that can be used for toolbars etc.
49818      * After modifying the contents of this panel a call to grid.autoSize() may be
49819      * required to register any changes in size.
49820      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
49821      * @return Roo.Element
49822      */
49823     getFooterPanel : function(doShow){
49824         if(doShow){
49825             this.footerPanel.show();
49826         }
49827         return this.footerPanel;
49828     },
49829
49830     initElements : function(){
49831         var E = Roo.Element;
49832         var el = this.grid.getGridEl().dom.firstChild;
49833         var cs = el.childNodes;
49834
49835         this.el = new E(el);
49836         
49837          this.focusEl = new E(el.firstChild);
49838         this.focusEl.swallowEvent("click", true);
49839         
49840         this.headerPanel = new E(cs[1]);
49841         this.headerPanel.enableDisplayMode("block");
49842
49843         this.scroller = new E(cs[2]);
49844         this.scrollSizer = new E(this.scroller.dom.firstChild);
49845
49846         this.lockedWrap = new E(cs[3]);
49847         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
49848         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
49849
49850         this.mainWrap = new E(cs[4]);
49851         this.mainHd = new E(this.mainWrap.dom.firstChild);
49852         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
49853
49854         this.footerPanel = new E(cs[5]);
49855         this.footerPanel.enableDisplayMode("block");
49856
49857         this.resizeProxy = new E(cs[6]);
49858
49859         this.headerSelector = String.format(
49860            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
49861            this.lockedHd.id, this.mainHd.id
49862         );
49863
49864         this.splitterSelector = String.format(
49865            '#{0} div.x-grid-split, #{1} div.x-grid-split',
49866            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
49867         );
49868     },
49869     idToCssName : function(s)
49870     {
49871         return s.replace(/[^a-z0-9]+/ig, '-');
49872     },
49873
49874     getHeaderCell : function(index){
49875         return Roo.DomQuery.select(this.headerSelector)[index];
49876     },
49877
49878     getHeaderCellMeasure : function(index){
49879         return this.getHeaderCell(index).firstChild;
49880     },
49881
49882     getHeaderCellText : function(index){
49883         return this.getHeaderCell(index).firstChild.firstChild;
49884     },
49885
49886     getLockedTable : function(){
49887         return this.lockedBody.dom.firstChild;
49888     },
49889
49890     getBodyTable : function(){
49891         return this.mainBody.dom.firstChild;
49892     },
49893
49894     getLockedRow : function(index){
49895         return this.getLockedTable().rows[index];
49896     },
49897
49898     getRow : function(index){
49899         return this.getBodyTable().rows[index];
49900     },
49901
49902     getRowComposite : function(index){
49903         if(!this.rowEl){
49904             this.rowEl = new Roo.CompositeElementLite();
49905         }
49906         var els = [], lrow, mrow;
49907         if(lrow = this.getLockedRow(index)){
49908             els.push(lrow);
49909         }
49910         if(mrow = this.getRow(index)){
49911             els.push(mrow);
49912         }
49913         this.rowEl.elements = els;
49914         return this.rowEl;
49915     },
49916     /**
49917      * Gets the 'td' of the cell
49918      * 
49919      * @param {Integer} rowIndex row to select
49920      * @param {Integer} colIndex column to select
49921      * 
49922      * @return {Object} 
49923      */
49924     getCell : function(rowIndex, colIndex){
49925         var locked = this.cm.getLockedCount();
49926         var source;
49927         if(colIndex < locked){
49928             source = this.lockedBody.dom.firstChild;
49929         }else{
49930             source = this.mainBody.dom.firstChild;
49931             colIndex -= locked;
49932         }
49933         return source.rows[rowIndex].childNodes[colIndex];
49934     },
49935
49936     getCellText : function(rowIndex, colIndex){
49937         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
49938     },
49939
49940     getCellBox : function(cell){
49941         var b = this.fly(cell).getBox();
49942         if(Roo.isOpera){ // opera fails to report the Y
49943             b.y = cell.offsetTop + this.mainBody.getY();
49944         }
49945         return b;
49946     },
49947
49948     getCellIndex : function(cell){
49949         var id = String(cell.className).match(this.cellRE);
49950         if(id){
49951             return parseInt(id[1], 10);
49952         }
49953         return 0;
49954     },
49955
49956     findHeaderIndex : function(n){
49957         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
49958         return r ? this.getCellIndex(r) : false;
49959     },
49960
49961     findHeaderCell : function(n){
49962         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
49963         return r ? r : false;
49964     },
49965
49966     findRowIndex : function(n){
49967         if(!n){
49968             return false;
49969         }
49970         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
49971         return r ? r.rowIndex : false;
49972     },
49973
49974     findCellIndex : function(node){
49975         var stop = this.el.dom;
49976         while(node && node != stop){
49977             if(this.findRE.test(node.className)){
49978                 return this.getCellIndex(node);
49979             }
49980             node = node.parentNode;
49981         }
49982         return false;
49983     },
49984
49985     getColumnId : function(index){
49986         return this.cm.getColumnId(index);
49987     },
49988
49989     getSplitters : function()
49990     {
49991         if(this.splitterSelector){
49992            return Roo.DomQuery.select(this.splitterSelector);
49993         }else{
49994             return null;
49995       }
49996     },
49997
49998     getSplitter : function(index){
49999         return this.getSplitters()[index];
50000     },
50001
50002     onRowOver : function(e, t){
50003         var row;
50004         if((row = this.findRowIndex(t)) !== false){
50005             this.getRowComposite(row).addClass("x-grid-row-over");
50006         }
50007     },
50008
50009     onRowOut : function(e, t){
50010         var row;
50011         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
50012             this.getRowComposite(row).removeClass("x-grid-row-over");
50013         }
50014     },
50015
50016     renderHeaders : function(){
50017         var cm = this.cm;
50018         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
50019         var cb = [], lb = [], sb = [], lsb = [], p = {};
50020         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50021             p.cellId = "x-grid-hd-0-" + i;
50022             p.splitId = "x-grid-csplit-0-" + i;
50023             p.id = cm.getColumnId(i);
50024             p.title = cm.getColumnTooltip(i) || "";
50025             p.value = cm.getColumnHeader(i) || "";
50026             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
50027             if(!cm.isLocked(i)){
50028                 cb[cb.length] = ct.apply(p);
50029                 sb[sb.length] = st.apply(p);
50030             }else{
50031                 lb[lb.length] = ct.apply(p);
50032                 lsb[lsb.length] = st.apply(p);
50033             }
50034         }
50035         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
50036                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
50037     },
50038
50039     updateHeaders : function(){
50040         var html = this.renderHeaders();
50041         this.lockedHd.update(html[0]);
50042         this.mainHd.update(html[1]);
50043     },
50044
50045     /**
50046      * Focuses the specified row.
50047      * @param {Number} row The row index
50048      */
50049     focusRow : function(row)
50050     {
50051         //Roo.log('GridView.focusRow');
50052         var x = this.scroller.dom.scrollLeft;
50053         this.focusCell(row, 0, false);
50054         this.scroller.dom.scrollLeft = x;
50055     },
50056
50057     /**
50058      * Focuses the specified cell.
50059      * @param {Number} row The row index
50060      * @param {Number} col The column index
50061      * @param {Boolean} hscroll false to disable horizontal scrolling
50062      */
50063     focusCell : function(row, col, hscroll)
50064     {
50065         //Roo.log('GridView.focusCell');
50066         var el = this.ensureVisible(row, col, hscroll);
50067         this.focusEl.alignTo(el, "tl-tl");
50068         if(Roo.isGecko){
50069             this.focusEl.focus();
50070         }else{
50071             this.focusEl.focus.defer(1, this.focusEl);
50072         }
50073     },
50074
50075     /**
50076      * Scrolls the specified cell into view
50077      * @param {Number} row The row index
50078      * @param {Number} col The column index
50079      * @param {Boolean} hscroll false to disable horizontal scrolling
50080      */
50081     ensureVisible : function(row, col, hscroll)
50082     {
50083         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
50084         //return null; //disable for testing.
50085         if(typeof row != "number"){
50086             row = row.rowIndex;
50087         }
50088         if(row < 0 && row >= this.ds.getCount()){
50089             return  null;
50090         }
50091         col = (col !== undefined ? col : 0);
50092         var cm = this.grid.colModel;
50093         while(cm.isHidden(col)){
50094             col++;
50095         }
50096
50097         var el = this.getCell(row, col);
50098         if(!el){
50099             return null;
50100         }
50101         var c = this.scroller.dom;
50102
50103         var ctop = parseInt(el.offsetTop, 10);
50104         var cleft = parseInt(el.offsetLeft, 10);
50105         var cbot = ctop + el.offsetHeight;
50106         var cright = cleft + el.offsetWidth;
50107         
50108         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
50109         var stop = parseInt(c.scrollTop, 10);
50110         var sleft = parseInt(c.scrollLeft, 10);
50111         var sbot = stop + ch;
50112         var sright = sleft + c.clientWidth;
50113         /*
50114         Roo.log('GridView.ensureVisible:' +
50115                 ' ctop:' + ctop +
50116                 ' c.clientHeight:' + c.clientHeight +
50117                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
50118                 ' stop:' + stop +
50119                 ' cbot:' + cbot +
50120                 ' sbot:' + sbot +
50121                 ' ch:' + ch  
50122                 );
50123         */
50124         if(ctop < stop){
50125              c.scrollTop = ctop;
50126             //Roo.log("set scrolltop to ctop DISABLE?");
50127         }else if(cbot > sbot){
50128             //Roo.log("set scrolltop to cbot-ch");
50129             c.scrollTop = cbot-ch;
50130         }
50131         
50132         if(hscroll !== false){
50133             if(cleft < sleft){
50134                 c.scrollLeft = cleft;
50135             }else if(cright > sright){
50136                 c.scrollLeft = cright-c.clientWidth;
50137             }
50138         }
50139          
50140         return el;
50141     },
50142
50143     updateColumns : function(){
50144         this.grid.stopEditing();
50145         var cm = this.grid.colModel, colIds = this.getColumnIds();
50146         //var totalWidth = cm.getTotalWidth();
50147         var pos = 0;
50148         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50149             //if(cm.isHidden(i)) continue;
50150             var w = cm.getColumnWidth(i);
50151             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
50152             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
50153         }
50154         this.updateSplitters();
50155     },
50156
50157     generateRules : function(cm){
50158         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
50159         Roo.util.CSS.removeStyleSheet(rulesId);
50160         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50161             var cid = cm.getColumnId(i);
50162             var align = '';
50163             if(cm.config[i].align){
50164                 align = 'text-align:'+cm.config[i].align+';';
50165             }
50166             var hidden = '';
50167             if(cm.isHidden(i)){
50168                 hidden = 'display:none;';
50169             }
50170             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
50171             ruleBuf.push(
50172                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
50173                     this.hdSelector, cid, " {\n", align, width, "}\n",
50174                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
50175                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
50176         }
50177         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
50178     },
50179
50180     updateSplitters : function(){
50181         var cm = this.cm, s = this.getSplitters();
50182         if(s){ // splitters not created yet
50183             var pos = 0, locked = true;
50184             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50185                 if(cm.isHidden(i)) continue;
50186                 var w = cm.getColumnWidth(i); // make sure it's a number
50187                 if(!cm.isLocked(i) && locked){
50188                     pos = 0;
50189                     locked = false;
50190                 }
50191                 pos += w;
50192                 s[i].style.left = (pos-this.splitOffset) + "px";
50193             }
50194         }
50195     },
50196
50197     handleHiddenChange : function(colModel, colIndex, hidden){
50198         if(hidden){
50199             this.hideColumn(colIndex);
50200         }else{
50201             this.unhideColumn(colIndex);
50202         }
50203     },
50204
50205     hideColumn : function(colIndex){
50206         var cid = this.getColumnId(colIndex);
50207         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
50208         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
50209         if(Roo.isSafari){
50210             this.updateHeaders();
50211         }
50212         this.updateSplitters();
50213         this.layout();
50214     },
50215
50216     unhideColumn : function(colIndex){
50217         var cid = this.getColumnId(colIndex);
50218         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
50219         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
50220
50221         if(Roo.isSafari){
50222             this.updateHeaders();
50223         }
50224         this.updateSplitters();
50225         this.layout();
50226     },
50227
50228     insertRows : function(dm, firstRow, lastRow, isUpdate){
50229         if(firstRow == 0 && lastRow == dm.getCount()-1){
50230             this.refresh();
50231         }else{
50232             if(!isUpdate){
50233                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
50234             }
50235             var s = this.getScrollState();
50236             var markup = this.renderRows(firstRow, lastRow);
50237             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
50238             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
50239             this.restoreScroll(s);
50240             if(!isUpdate){
50241                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
50242                 this.syncRowHeights(firstRow, lastRow);
50243                 this.stripeRows(firstRow);
50244                 this.layout();
50245             }
50246         }
50247     },
50248
50249     bufferRows : function(markup, target, index){
50250         var before = null, trows = target.rows, tbody = target.tBodies[0];
50251         if(index < trows.length){
50252             before = trows[index];
50253         }
50254         var b = document.createElement("div");
50255         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
50256         var rows = b.firstChild.rows;
50257         for(var i = 0, len = rows.length; i < len; i++){
50258             if(before){
50259                 tbody.insertBefore(rows[0], before);
50260             }else{
50261                 tbody.appendChild(rows[0]);
50262             }
50263         }
50264         b.innerHTML = "";
50265         b = null;
50266     },
50267
50268     deleteRows : function(dm, firstRow, lastRow){
50269         if(dm.getRowCount()<1){
50270             this.fireEvent("beforerefresh", this);
50271             this.mainBody.update("");
50272             this.lockedBody.update("");
50273             this.fireEvent("refresh", this);
50274         }else{
50275             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
50276             var bt = this.getBodyTable();
50277             var tbody = bt.firstChild;
50278             var rows = bt.rows;
50279             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
50280                 tbody.removeChild(rows[firstRow]);
50281             }
50282             this.stripeRows(firstRow);
50283             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
50284         }
50285     },
50286
50287     updateRows : function(dataSource, firstRow, lastRow){
50288         var s = this.getScrollState();
50289         this.refresh();
50290         this.restoreScroll(s);
50291     },
50292
50293     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
50294         if(!noRefresh){
50295            this.refresh();
50296         }
50297         this.updateHeaderSortState();
50298     },
50299
50300     getScrollState : function(){
50301         
50302         var sb = this.scroller.dom;
50303         return {left: sb.scrollLeft, top: sb.scrollTop};
50304     },
50305
50306     stripeRows : function(startRow){
50307         if(!this.grid.stripeRows || this.ds.getCount() < 1){
50308             return;
50309         }
50310         startRow = startRow || 0;
50311         var rows = this.getBodyTable().rows;
50312         var lrows = this.getLockedTable().rows;
50313         var cls = ' x-grid-row-alt ';
50314         for(var i = startRow, len = rows.length; i < len; i++){
50315             var row = rows[i], lrow = lrows[i];
50316             var isAlt = ((i+1) % 2 == 0);
50317             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
50318             if(isAlt == hasAlt){
50319                 continue;
50320             }
50321             if(isAlt){
50322                 row.className += " x-grid-row-alt";
50323             }else{
50324                 row.className = row.className.replace("x-grid-row-alt", "");
50325             }
50326             if(lrow){
50327                 lrow.className = row.className;
50328             }
50329         }
50330     },
50331
50332     restoreScroll : function(state){
50333         //Roo.log('GridView.restoreScroll');
50334         var sb = this.scroller.dom;
50335         sb.scrollLeft = state.left;
50336         sb.scrollTop = state.top;
50337         this.syncScroll();
50338     },
50339
50340     syncScroll : function(){
50341         //Roo.log('GridView.syncScroll');
50342         var sb = this.scroller.dom;
50343         var sh = this.mainHd.dom;
50344         var bs = this.mainBody.dom;
50345         var lv = this.lockedBody.dom;
50346         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
50347         lv.scrollTop = bs.scrollTop = sb.scrollTop;
50348     },
50349
50350     handleScroll : function(e){
50351         this.syncScroll();
50352         var sb = this.scroller.dom;
50353         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
50354         e.stopEvent();
50355     },
50356
50357     handleWheel : function(e){
50358         var d = e.getWheelDelta();
50359         this.scroller.dom.scrollTop -= d*22;
50360         // set this here to prevent jumpy scrolling on large tables
50361         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
50362         e.stopEvent();
50363     },
50364
50365     renderRows : function(startRow, endRow){
50366         // pull in all the crap needed to render rows
50367         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
50368         var colCount = cm.getColumnCount();
50369
50370         if(ds.getCount() < 1){
50371             return ["", ""];
50372         }
50373
50374         // build a map for all the columns
50375         var cs = [];
50376         for(var i = 0; i < colCount; i++){
50377             var name = cm.getDataIndex(i);
50378             cs[i] = {
50379                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
50380                 renderer : cm.getRenderer(i),
50381                 id : cm.getColumnId(i),
50382                 locked : cm.isLocked(i)
50383             };
50384         }
50385
50386         startRow = startRow || 0;
50387         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
50388
50389         // records to render
50390         var rs = ds.getRange(startRow, endRow);
50391
50392         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
50393     },
50394
50395     // As much as I hate to duplicate code, this was branched because FireFox really hates
50396     // [].join("") on strings. The performance difference was substantial enough to
50397     // branch this function
50398     doRender : Roo.isGecko ?
50399             function(cs, rs, ds, startRow, colCount, stripe){
50400                 var ts = this.templates, ct = ts.cell, rt = ts.row;
50401                 // buffers
50402                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
50403                 
50404                 var hasListener = this.grid.hasListener('rowclass');
50405                 var rowcfg = {};
50406                 for(var j = 0, len = rs.length; j < len; j++){
50407                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
50408                     for(var i = 0; i < colCount; i++){
50409                         c = cs[i];
50410                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
50411                         p.id = c.id;
50412                         p.css = p.attr = "";
50413                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
50414                         if(p.value == undefined || p.value === "") p.value = "&#160;";
50415                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
50416                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
50417                         }
50418                         var markup = ct.apply(p);
50419                         if(!c.locked){
50420                             cb+= markup;
50421                         }else{
50422                             lcb+= markup;
50423                         }
50424                     }
50425                     var alt = [];
50426                     if(stripe && ((rowIndex+1) % 2 == 0)){
50427                         alt.push("x-grid-row-alt")
50428                     }
50429                     if(r.dirty){
50430                         alt.push(  " x-grid-dirty-row");
50431                     }
50432                     rp.cells = lcb;
50433                     if(this.getRowClass){
50434                         alt.push(this.getRowClass(r, rowIndex));
50435                     }
50436                     if (hasListener) {
50437                         rowcfg = {
50438                              
50439                             record: r,
50440                             rowIndex : rowIndex,
50441                             rowClass : ''
50442                         }
50443                         this.grid.fireEvent('rowclass', this, rowcfg);
50444                         alt.push(rowcfg.rowClass);
50445                     }
50446                     rp.alt = alt.join(" ");
50447                     lbuf+= rt.apply(rp);
50448                     rp.cells = cb;
50449                     buf+=  rt.apply(rp);
50450                 }
50451                 return [lbuf, buf];
50452             } :
50453             function(cs, rs, ds, startRow, colCount, stripe){
50454                 var ts = this.templates, ct = ts.cell, rt = ts.row;
50455                 // buffers
50456                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
50457                 var hasListener = this.grid.hasListener('rowclass');
50458  
50459                 var rowcfg = {};
50460                 for(var j = 0, len = rs.length; j < len; j++){
50461                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
50462                     for(var i = 0; i < colCount; i++){
50463                         c = cs[i];
50464                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
50465                         p.id = c.id;
50466                         p.css = p.attr = "";
50467                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
50468                         if(p.value == undefined || p.value === "") p.value = "&#160;";
50469                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
50470                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
50471                         }
50472                         
50473                         var markup = ct.apply(p);
50474                         if(!c.locked){
50475                             cb[cb.length] = markup;
50476                         }else{
50477                             lcb[lcb.length] = markup;
50478                         }
50479                     }
50480                     var alt = [];
50481                     if(stripe && ((rowIndex+1) % 2 == 0)){
50482                         alt.push( "x-grid-row-alt");
50483                     }
50484                     if(r.dirty){
50485                         alt.push(" x-grid-dirty-row");
50486                     }
50487                     rp.cells = lcb;
50488                     if(this.getRowClass){
50489                         alt.push( this.getRowClass(r, rowIndex));
50490                     }
50491                     if (hasListener) {
50492                         rowcfg = {
50493                              
50494                             record: r,
50495                             rowIndex : rowIndex,
50496                             rowClass : ''
50497                         }
50498                         this.grid.fireEvent('rowclass', this, rowcfg);
50499                         alt.push(rowcfg.rowClass);
50500                     }
50501                     rp.alt = alt.join(" ");
50502                     rp.cells = lcb.join("");
50503                     lbuf[lbuf.length] = rt.apply(rp);
50504                     rp.cells = cb.join("");
50505                     buf[buf.length] =  rt.apply(rp);
50506                 }
50507                 return [lbuf.join(""), buf.join("")];
50508             },
50509
50510     renderBody : function(){
50511         var markup = this.renderRows();
50512         var bt = this.templates.body;
50513         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
50514     },
50515
50516     /**
50517      * Refreshes the grid
50518      * @param {Boolean} headersToo
50519      */
50520     refresh : function(headersToo){
50521         this.fireEvent("beforerefresh", this);
50522         this.grid.stopEditing();
50523         var result = this.renderBody();
50524         this.lockedBody.update(result[0]);
50525         this.mainBody.update(result[1]);
50526         if(headersToo === true){
50527             this.updateHeaders();
50528             this.updateColumns();
50529             this.updateSplitters();
50530             this.updateHeaderSortState();
50531         }
50532         this.syncRowHeights();
50533         this.layout();
50534         this.fireEvent("refresh", this);
50535     },
50536
50537     handleColumnMove : function(cm, oldIndex, newIndex){
50538         this.indexMap = null;
50539         var s = this.getScrollState();
50540         this.refresh(true);
50541         this.restoreScroll(s);
50542         this.afterMove(newIndex);
50543     },
50544
50545     afterMove : function(colIndex){
50546         if(this.enableMoveAnim && Roo.enableFx){
50547             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
50548         }
50549         // if multisort - fix sortOrder, and reload..
50550         if (this.grid.dataSource.multiSort) {
50551             // the we can call sort again..
50552             var dm = this.grid.dataSource;
50553             var cm = this.grid.colModel;
50554             var so = [];
50555             for(var i = 0; i < cm.config.length; i++ ) {
50556                 
50557                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
50558                     continue; // dont' bother, it's not in sort list or being set.
50559                 }
50560                 
50561                 so.push(cm.config[i].dataIndex);
50562             };
50563             dm.sortOrder = so;
50564             dm.load(dm.lastOptions);
50565             
50566             
50567         }
50568         
50569     },
50570
50571     updateCell : function(dm, rowIndex, dataIndex){
50572         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
50573         if(typeof colIndex == "undefined"){ // not present in grid
50574             return;
50575         }
50576         var cm = this.grid.colModel;
50577         var cell = this.getCell(rowIndex, colIndex);
50578         var cellText = this.getCellText(rowIndex, colIndex);
50579
50580         var p = {
50581             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
50582             id : cm.getColumnId(colIndex),
50583             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
50584         };
50585         var renderer = cm.getRenderer(colIndex);
50586         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
50587         if(typeof val == "undefined" || val === "") val = "&#160;";
50588         cellText.innerHTML = val;
50589         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
50590         this.syncRowHeights(rowIndex, rowIndex);
50591     },
50592
50593     calcColumnWidth : function(colIndex, maxRowsToMeasure){
50594         var maxWidth = 0;
50595         if(this.grid.autoSizeHeaders){
50596             var h = this.getHeaderCellMeasure(colIndex);
50597             maxWidth = Math.max(maxWidth, h.scrollWidth);
50598         }
50599         var tb, index;
50600         if(this.cm.isLocked(colIndex)){
50601             tb = this.getLockedTable();
50602             index = colIndex;
50603         }else{
50604             tb = this.getBodyTable();
50605             index = colIndex - this.cm.getLockedCount();
50606         }
50607         if(tb && tb.rows){
50608             var rows = tb.rows;
50609             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
50610             for(var i = 0; i < stopIndex; i++){
50611                 var cell = rows[i].childNodes[index].firstChild;
50612                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
50613             }
50614         }
50615         return maxWidth + /*margin for error in IE*/ 5;
50616     },
50617     /**
50618      * Autofit a column to its content.
50619      * @param {Number} colIndex
50620      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
50621      */
50622      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
50623          if(this.cm.isHidden(colIndex)){
50624              return; // can't calc a hidden column
50625          }
50626         if(forceMinSize){
50627             var cid = this.cm.getColumnId(colIndex);
50628             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
50629            if(this.grid.autoSizeHeaders){
50630                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
50631            }
50632         }
50633         var newWidth = this.calcColumnWidth(colIndex);
50634         this.cm.setColumnWidth(colIndex,
50635             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
50636         if(!suppressEvent){
50637             this.grid.fireEvent("columnresize", colIndex, newWidth);
50638         }
50639     },
50640
50641     /**
50642      * Autofits all columns to their content and then expands to fit any extra space in the grid
50643      */
50644      autoSizeColumns : function(){
50645         var cm = this.grid.colModel;
50646         var colCount = cm.getColumnCount();
50647         for(var i = 0; i < colCount; i++){
50648             this.autoSizeColumn(i, true, true);
50649         }
50650         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
50651             this.fitColumns();
50652         }else{
50653             this.updateColumns();
50654             this.layout();
50655         }
50656     },
50657
50658     /**
50659      * Autofits all columns to the grid's width proportionate with their current size
50660      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
50661      */
50662     fitColumns : function(reserveScrollSpace){
50663         var cm = this.grid.colModel;
50664         var colCount = cm.getColumnCount();
50665         var cols = [];
50666         var width = 0;
50667         var i, w;
50668         for (i = 0; i < colCount; i++){
50669             if(!cm.isHidden(i) && !cm.isFixed(i)){
50670                 w = cm.getColumnWidth(i);
50671                 cols.push(i);
50672                 cols.push(w);
50673                 width += w;
50674             }
50675         }
50676         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
50677         if(reserveScrollSpace){
50678             avail -= 17;
50679         }
50680         var frac = (avail - cm.getTotalWidth())/width;
50681         while (cols.length){
50682             w = cols.pop();
50683             i = cols.pop();
50684             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
50685         }
50686         this.updateColumns();
50687         this.layout();
50688     },
50689
50690     onRowSelect : function(rowIndex){
50691         var row = this.getRowComposite(rowIndex);
50692         row.addClass("x-grid-row-selected");
50693     },
50694
50695     onRowDeselect : function(rowIndex){
50696         var row = this.getRowComposite(rowIndex);
50697         row.removeClass("x-grid-row-selected");
50698     },
50699
50700     onCellSelect : function(row, col){
50701         var cell = this.getCell(row, col);
50702         if(cell){
50703             Roo.fly(cell).addClass("x-grid-cell-selected");
50704         }
50705     },
50706
50707     onCellDeselect : function(row, col){
50708         var cell = this.getCell(row, col);
50709         if(cell){
50710             Roo.fly(cell).removeClass("x-grid-cell-selected");
50711         }
50712     },
50713
50714     updateHeaderSortState : function(){
50715         
50716         // sort state can be single { field: xxx, direction : yyy}
50717         // or   { xxx=>ASC , yyy : DESC ..... }
50718         
50719         var mstate = {};
50720         if (!this.ds.multiSort) { 
50721             var state = this.ds.getSortState();
50722             if(!state){
50723                 return;
50724             }
50725             mstate[state.field] = state.direction;
50726             // FIXME... - this is not used here.. but might be elsewhere..
50727             this.sortState = state;
50728             
50729         } else {
50730             mstate = this.ds.sortToggle;
50731         }
50732         //remove existing sort classes..
50733         
50734         var sc = this.sortClasses;
50735         var hds = this.el.select(this.headerSelector).removeClass(sc);
50736         
50737         for(var f in mstate) {
50738         
50739             var sortColumn = this.cm.findColumnIndex(f);
50740             
50741             if(sortColumn != -1){
50742                 var sortDir = mstate[f];        
50743                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
50744             }
50745         }
50746         
50747          
50748         
50749     },
50750
50751
50752     handleHeaderClick : function(g, index){
50753         if(this.headersDisabled){
50754             return;
50755         }
50756         var dm = g.dataSource, cm = g.colModel;
50757         if(!cm.isSortable(index)){
50758             return;
50759         }
50760         g.stopEditing();
50761         
50762         if (dm.multiSort) {
50763             // update the sortOrder
50764             var so = [];
50765             for(var i = 0; i < cm.config.length; i++ ) {
50766                 
50767                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
50768                     continue; // dont' bother, it's not in sort list or being set.
50769                 }
50770                 
50771                 so.push(cm.config[i].dataIndex);
50772             };
50773             dm.sortOrder = so;
50774         }
50775         
50776         
50777         dm.sort(cm.getDataIndex(index));
50778     },
50779
50780
50781     destroy : function(){
50782         if(this.colMenu){
50783             this.colMenu.removeAll();
50784             Roo.menu.MenuMgr.unregister(this.colMenu);
50785             this.colMenu.getEl().remove();
50786             delete this.colMenu;
50787         }
50788         if(this.hmenu){
50789             this.hmenu.removeAll();
50790             Roo.menu.MenuMgr.unregister(this.hmenu);
50791             this.hmenu.getEl().remove();
50792             delete this.hmenu;
50793         }
50794         if(this.grid.enableColumnMove){
50795             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
50796             if(dds){
50797                 for(var dd in dds){
50798                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
50799                         var elid = dds[dd].dragElId;
50800                         dds[dd].unreg();
50801                         Roo.get(elid).remove();
50802                     } else if(dds[dd].config.isTarget){
50803                         dds[dd].proxyTop.remove();
50804                         dds[dd].proxyBottom.remove();
50805                         dds[dd].unreg();
50806                     }
50807                     if(Roo.dd.DDM.locationCache[dd]){
50808                         delete Roo.dd.DDM.locationCache[dd];
50809                     }
50810                 }
50811                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
50812             }
50813         }
50814         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
50815         this.bind(null, null);
50816         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
50817     },
50818
50819     handleLockChange : function(){
50820         this.refresh(true);
50821     },
50822
50823     onDenyColumnLock : function(){
50824
50825     },
50826
50827     onDenyColumnHide : function(){
50828
50829     },
50830
50831     handleHdMenuClick : function(item){
50832         var index = this.hdCtxIndex;
50833         var cm = this.cm, ds = this.ds;
50834         switch(item.id){
50835             case "asc":
50836                 ds.sort(cm.getDataIndex(index), "ASC");
50837                 break;
50838             case "desc":
50839                 ds.sort(cm.getDataIndex(index), "DESC");
50840                 break;
50841             case "lock":
50842                 var lc = cm.getLockedCount();
50843                 if(cm.getColumnCount(true) <= lc+1){
50844                     this.onDenyColumnLock();
50845                     return;
50846                 }
50847                 if(lc != index){
50848                     cm.setLocked(index, true, true);
50849                     cm.moveColumn(index, lc);
50850                     this.grid.fireEvent("columnmove", index, lc);
50851                 }else{
50852                     cm.setLocked(index, true);
50853                 }
50854             break;
50855             case "unlock":
50856                 var lc = cm.getLockedCount();
50857                 if((lc-1) != index){
50858                     cm.setLocked(index, false, true);
50859                     cm.moveColumn(index, lc-1);
50860                     this.grid.fireEvent("columnmove", index, lc-1);
50861                 }else{
50862                     cm.setLocked(index, false);
50863                 }
50864             break;
50865             default:
50866                 index = cm.getIndexById(item.id.substr(4));
50867                 if(index != -1){
50868                     if(item.checked && cm.getColumnCount(true) <= 1){
50869                         this.onDenyColumnHide();
50870                         return false;
50871                     }
50872                     cm.setHidden(index, item.checked);
50873                 }
50874         }
50875         return true;
50876     },
50877
50878     beforeColMenuShow : function(){
50879         var cm = this.cm,  colCount = cm.getColumnCount();
50880         this.colMenu.removeAll();
50881         for(var i = 0; i < colCount; i++){
50882             this.colMenu.add(new Roo.menu.CheckItem({
50883                 id: "col-"+cm.getColumnId(i),
50884                 text: cm.getColumnHeader(i),
50885                 checked: !cm.isHidden(i),
50886                 hideOnClick:false
50887             }));
50888         }
50889     },
50890
50891     handleHdCtx : function(g, index, e){
50892         e.stopEvent();
50893         var hd = this.getHeaderCell(index);
50894         this.hdCtxIndex = index;
50895         var ms = this.hmenu.items, cm = this.cm;
50896         ms.get("asc").setDisabled(!cm.isSortable(index));
50897         ms.get("desc").setDisabled(!cm.isSortable(index));
50898         if(this.grid.enableColLock !== false){
50899             ms.get("lock").setDisabled(cm.isLocked(index));
50900             ms.get("unlock").setDisabled(!cm.isLocked(index));
50901         }
50902         this.hmenu.show(hd, "tl-bl");
50903     },
50904
50905     handleHdOver : function(e){
50906         var hd = this.findHeaderCell(e.getTarget());
50907         if(hd && !this.headersDisabled){
50908             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
50909                this.fly(hd).addClass("x-grid-hd-over");
50910             }
50911         }
50912     },
50913
50914     handleHdOut : function(e){
50915         var hd = this.findHeaderCell(e.getTarget());
50916         if(hd){
50917             this.fly(hd).removeClass("x-grid-hd-over");
50918         }
50919     },
50920
50921     handleSplitDblClick : function(e, t){
50922         var i = this.getCellIndex(t);
50923         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
50924             this.autoSizeColumn(i, true);
50925             this.layout();
50926         }
50927     },
50928
50929     render : function(){
50930
50931         var cm = this.cm;
50932         var colCount = cm.getColumnCount();
50933
50934         if(this.grid.monitorWindowResize === true){
50935             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50936         }
50937         var header = this.renderHeaders();
50938         var body = this.templates.body.apply({rows:""});
50939         var html = this.templates.master.apply({
50940             lockedBody: body,
50941             body: body,
50942             lockedHeader: header[0],
50943             header: header[1]
50944         });
50945
50946         //this.updateColumns();
50947
50948         this.grid.getGridEl().dom.innerHTML = html;
50949
50950         this.initElements();
50951         
50952         // a kludge to fix the random scolling effect in webkit
50953         this.el.on("scroll", function() {
50954             this.el.dom.scrollTop=0; // hopefully not recursive..
50955         },this);
50956
50957         this.scroller.on("scroll", this.handleScroll, this);
50958         this.lockedBody.on("mousewheel", this.handleWheel, this);
50959         this.mainBody.on("mousewheel", this.handleWheel, this);
50960
50961         this.mainHd.on("mouseover", this.handleHdOver, this);
50962         this.mainHd.on("mouseout", this.handleHdOut, this);
50963         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
50964                 {delegate: "."+this.splitClass});
50965
50966         this.lockedHd.on("mouseover", this.handleHdOver, this);
50967         this.lockedHd.on("mouseout", this.handleHdOut, this);
50968         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
50969                 {delegate: "."+this.splitClass});
50970
50971         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
50972             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50973         }
50974
50975         this.updateSplitters();
50976
50977         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
50978             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50979             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50980         }
50981
50982         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
50983             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
50984             this.hmenu.add(
50985                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
50986                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
50987             );
50988             if(this.grid.enableColLock !== false){
50989                 this.hmenu.add('-',
50990                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
50991                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
50992                 );
50993             }
50994             if(this.grid.enableColumnHide !== false){
50995
50996                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
50997                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
50998                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
50999
51000                 this.hmenu.add('-',
51001                     {id:"columns", text: this.columnsText, menu: this.colMenu}
51002                 );
51003             }
51004             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
51005
51006             this.grid.on("headercontextmenu", this.handleHdCtx, this);
51007         }
51008
51009         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
51010             this.dd = new Roo.grid.GridDragZone(this.grid, {
51011                 ddGroup : this.grid.ddGroup || 'GridDD'
51012             });
51013         }
51014
51015         /*
51016         for(var i = 0; i < colCount; i++){
51017             if(cm.isHidden(i)){
51018                 this.hideColumn(i);
51019             }
51020             if(cm.config[i].align){
51021                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
51022                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
51023             }
51024         }*/
51025         
51026         this.updateHeaderSortState();
51027
51028         this.beforeInitialResize();
51029         this.layout(true);
51030
51031         // two part rendering gives faster view to the user
51032         this.renderPhase2.defer(1, this);
51033     },
51034
51035     renderPhase2 : function(){
51036         // render the rows now
51037         this.refresh();
51038         if(this.grid.autoSizeColumns){
51039             this.autoSizeColumns();
51040         }
51041     },
51042
51043     beforeInitialResize : function(){
51044
51045     },
51046
51047     onColumnSplitterMoved : function(i, w){
51048         this.userResized = true;
51049         var cm = this.grid.colModel;
51050         cm.setColumnWidth(i, w, true);
51051         var cid = cm.getColumnId(i);
51052         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
51053         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
51054         this.updateSplitters();
51055         this.layout();
51056         this.grid.fireEvent("columnresize", i, w);
51057     },
51058
51059     syncRowHeights : function(startIndex, endIndex){
51060         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
51061             startIndex = startIndex || 0;
51062             var mrows = this.getBodyTable().rows;
51063             var lrows = this.getLockedTable().rows;
51064             var len = mrows.length-1;
51065             endIndex = Math.min(endIndex || len, len);
51066             for(var i = startIndex; i <= endIndex; i++){
51067                 var m = mrows[i], l = lrows[i];
51068                 var h = Math.max(m.offsetHeight, l.offsetHeight);
51069                 m.style.height = l.style.height = h + "px";
51070             }
51071         }
51072     },
51073
51074     layout : function(initialRender, is2ndPass){
51075         var g = this.grid;
51076         var auto = g.autoHeight;
51077         var scrollOffset = 16;
51078         var c = g.getGridEl(), cm = this.cm,
51079                 expandCol = g.autoExpandColumn,
51080                 gv = this;
51081         //c.beginMeasure();
51082
51083         if(!c.dom.offsetWidth){ // display:none?
51084             if(initialRender){
51085                 this.lockedWrap.show();
51086                 this.mainWrap.show();
51087             }
51088             return;
51089         }
51090
51091         var hasLock = this.cm.isLocked(0);
51092
51093         var tbh = this.headerPanel.getHeight();
51094         var bbh = this.footerPanel.getHeight();
51095
51096         if(auto){
51097             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
51098             var newHeight = ch + c.getBorderWidth("tb");
51099             if(g.maxHeight){
51100                 newHeight = Math.min(g.maxHeight, newHeight);
51101             }
51102             c.setHeight(newHeight);
51103         }
51104
51105         if(g.autoWidth){
51106             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
51107         }
51108
51109         var s = this.scroller;
51110
51111         var csize = c.getSize(true);
51112
51113         this.el.setSize(csize.width, csize.height);
51114
51115         this.headerPanel.setWidth(csize.width);
51116         this.footerPanel.setWidth(csize.width);
51117
51118         var hdHeight = this.mainHd.getHeight();
51119         var vw = csize.width;
51120         var vh = csize.height - (tbh + bbh);
51121
51122         s.setSize(vw, vh);
51123
51124         var bt = this.getBodyTable();
51125         var ltWidth = hasLock ?
51126                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
51127
51128         var scrollHeight = bt.offsetHeight;
51129         var scrollWidth = ltWidth + bt.offsetWidth;
51130         var vscroll = false, hscroll = false;
51131
51132         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
51133
51134         var lw = this.lockedWrap, mw = this.mainWrap;
51135         var lb = this.lockedBody, mb = this.mainBody;
51136
51137         setTimeout(function(){
51138             var t = s.dom.offsetTop;
51139             var w = s.dom.clientWidth,
51140                 h = s.dom.clientHeight;
51141
51142             lw.setTop(t);
51143             lw.setSize(ltWidth, h);
51144
51145             mw.setLeftTop(ltWidth, t);
51146             mw.setSize(w-ltWidth, h);
51147
51148             lb.setHeight(h-hdHeight);
51149             mb.setHeight(h-hdHeight);
51150
51151             if(is2ndPass !== true && !gv.userResized && expandCol){
51152                 // high speed resize without full column calculation
51153                 
51154                 var ci = cm.getIndexById(expandCol);
51155                 if (ci < 0) {
51156                     ci = cm.findColumnIndex(expandCol);
51157                 }
51158                 ci = Math.max(0, ci); // make sure it's got at least the first col.
51159                 var expandId = cm.getColumnId(ci);
51160                 var  tw = cm.getTotalWidth(false);
51161                 var currentWidth = cm.getColumnWidth(ci);
51162                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
51163                 if(currentWidth != cw){
51164                     cm.setColumnWidth(ci, cw, true);
51165                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
51166                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
51167                     gv.updateSplitters();
51168                     gv.layout(false, true);
51169                 }
51170             }
51171
51172             if(initialRender){
51173                 lw.show();
51174                 mw.show();
51175             }
51176             //c.endMeasure();
51177         }, 10);
51178     },
51179
51180     onWindowResize : function(){
51181         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
51182             return;
51183         }
51184         this.layout();
51185     },
51186
51187     appendFooter : function(parentEl){
51188         return null;
51189     },
51190
51191     sortAscText : "Sort Ascending",
51192     sortDescText : "Sort Descending",
51193     lockText : "Lock Column",
51194     unlockText : "Unlock Column",
51195     columnsText : "Columns"
51196 });
51197
51198
51199 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
51200     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
51201     this.proxy.el.addClass('x-grid3-col-dd');
51202 };
51203
51204 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
51205     handleMouseDown : function(e){
51206
51207     },
51208
51209     callHandleMouseDown : function(e){
51210         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
51211     }
51212 });
51213 /*
51214  * Based on:
51215  * Ext JS Library 1.1.1
51216  * Copyright(c) 2006-2007, Ext JS, LLC.
51217  *
51218  * Originally Released Under LGPL - original licence link has changed is not relivant.
51219  *
51220  * Fork - LGPL
51221  * <script type="text/javascript">
51222  */
51223  
51224 // private
51225 // This is a support class used internally by the Grid components
51226 Roo.grid.SplitDragZone = function(grid, hd, hd2){
51227     this.grid = grid;
51228     this.view = grid.getView();
51229     this.proxy = this.view.resizeProxy;
51230     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
51231         "gridSplitters" + this.grid.getGridEl().id, {
51232         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
51233     });
51234     this.setHandleElId(Roo.id(hd));
51235     this.setOuterHandleElId(Roo.id(hd2));
51236     this.scroll = false;
51237 };
51238 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
51239     fly: Roo.Element.fly,
51240
51241     b4StartDrag : function(x, y){
51242         this.view.headersDisabled = true;
51243         this.proxy.setHeight(this.view.mainWrap.getHeight());
51244         var w = this.cm.getColumnWidth(this.cellIndex);
51245         var minw = Math.max(w-this.grid.minColumnWidth, 0);
51246         this.resetConstraints();
51247         this.setXConstraint(minw, 1000);
51248         this.setYConstraint(0, 0);
51249         this.minX = x - minw;
51250         this.maxX = x + 1000;
51251         this.startPos = x;
51252         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
51253     },
51254
51255
51256     handleMouseDown : function(e){
51257         ev = Roo.EventObject.setEvent(e);
51258         var t = this.fly(ev.getTarget());
51259         if(t.hasClass("x-grid-split")){
51260             this.cellIndex = this.view.getCellIndex(t.dom);
51261             this.split = t.dom;
51262             this.cm = this.grid.colModel;
51263             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
51264                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
51265             }
51266         }
51267     },
51268
51269     endDrag : function(e){
51270         this.view.headersDisabled = false;
51271         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
51272         var diff = endX - this.startPos;
51273         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
51274     },
51275
51276     autoOffset : function(){
51277         this.setDelta(0,0);
51278     }
51279 });/*
51280  * Based on:
51281  * Ext JS Library 1.1.1
51282  * Copyright(c) 2006-2007, Ext JS, LLC.
51283  *
51284  * Originally Released Under LGPL - original licence link has changed is not relivant.
51285  *
51286  * Fork - LGPL
51287  * <script type="text/javascript">
51288  */
51289  
51290 // private
51291 // This is a support class used internally by the Grid components
51292 Roo.grid.GridDragZone = function(grid, config){
51293     this.view = grid.getView();
51294     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
51295     if(this.view.lockedBody){
51296         this.setHandleElId(Roo.id(this.view.mainBody.dom));
51297         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
51298     }
51299     this.scroll = false;
51300     this.grid = grid;
51301     this.ddel = document.createElement('div');
51302     this.ddel.className = 'x-grid-dd-wrap';
51303 };
51304
51305 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
51306     ddGroup : "GridDD",
51307
51308     getDragData : function(e){
51309         var t = Roo.lib.Event.getTarget(e);
51310         var rowIndex = this.view.findRowIndex(t);
51311         if(rowIndex !== false){
51312             var sm = this.grid.selModel;
51313             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
51314               //  sm.mouseDown(e, t);
51315             //}
51316             if (e.hasModifier()){
51317                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
51318             }
51319             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
51320         }
51321         return false;
51322     },
51323
51324     onInitDrag : function(e){
51325         var data = this.dragData;
51326         this.ddel.innerHTML = this.grid.getDragDropText();
51327         this.proxy.update(this.ddel);
51328         // fire start drag?
51329     },
51330
51331     afterRepair : function(){
51332         this.dragging = false;
51333     },
51334
51335     getRepairXY : function(e, data){
51336         return false;
51337     },
51338
51339     onEndDrag : function(data, e){
51340         // fire end drag?
51341     },
51342
51343     onValidDrop : function(dd, e, id){
51344         // fire drag drop?
51345         this.hideProxy();
51346     },
51347
51348     beforeInvalidDrop : function(e, id){
51349
51350     }
51351 });/*
51352  * Based on:
51353  * Ext JS Library 1.1.1
51354  * Copyright(c) 2006-2007, Ext JS, LLC.
51355  *
51356  * Originally Released Under LGPL - original licence link has changed is not relivant.
51357  *
51358  * Fork - LGPL
51359  * <script type="text/javascript">
51360  */
51361  
51362
51363 /**
51364  * @class Roo.grid.ColumnModel
51365  * @extends Roo.util.Observable
51366  * This is the default implementation of a ColumnModel used by the Grid. It defines
51367  * the columns in the grid.
51368  * <br>Usage:<br>
51369  <pre><code>
51370  var colModel = new Roo.grid.ColumnModel([
51371         {header: "Ticker", width: 60, sortable: true, locked: true},
51372         {header: "Company Name", width: 150, sortable: true},
51373         {header: "Market Cap.", width: 100, sortable: true},
51374         {header: "$ Sales", width: 100, sortable: true, renderer: money},
51375         {header: "Employees", width: 100, sortable: true, resizable: false}
51376  ]);
51377  </code></pre>
51378  * <p>
51379  
51380  * The config options listed for this class are options which may appear in each
51381  * individual column definition.
51382  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
51383  * @constructor
51384  * @param {Object} config An Array of column config objects. See this class's
51385  * config objects for details.
51386 */
51387 Roo.grid.ColumnModel = function(config){
51388         /**
51389      * The config passed into the constructor
51390      */
51391     this.config = config;
51392     this.lookup = {};
51393
51394     // if no id, create one
51395     // if the column does not have a dataIndex mapping,
51396     // map it to the order it is in the config
51397     for(var i = 0, len = config.length; i < len; i++){
51398         var c = config[i];
51399         if(typeof c.dataIndex == "undefined"){
51400             c.dataIndex = i;
51401         }
51402         if(typeof c.renderer == "string"){
51403             c.renderer = Roo.util.Format[c.renderer];
51404         }
51405         if(typeof c.id == "undefined"){
51406             c.id = Roo.id();
51407         }
51408         if(c.editor && c.editor.xtype){
51409             c.editor  = Roo.factory(c.editor, Roo.grid);
51410         }
51411         if(c.editor && c.editor.isFormField){
51412             c.editor = new Roo.grid.GridEditor(c.editor);
51413         }
51414         this.lookup[c.id] = c;
51415     }
51416
51417     /**
51418      * The width of columns which have no width specified (defaults to 100)
51419      * @type Number
51420      */
51421     this.defaultWidth = 100;
51422
51423     /**
51424      * Default sortable of columns which have no sortable specified (defaults to false)
51425      * @type Boolean
51426      */
51427     this.defaultSortable = false;
51428
51429     this.addEvents({
51430         /**
51431              * @event widthchange
51432              * Fires when the width of a column changes.
51433              * @param {ColumnModel} this
51434              * @param {Number} columnIndex The column index
51435              * @param {Number} newWidth The new width
51436              */
51437             "widthchange": true,
51438         /**
51439              * @event headerchange
51440              * Fires when the text of a header changes.
51441              * @param {ColumnModel} this
51442              * @param {Number} columnIndex The column index
51443              * @param {Number} newText The new header text
51444              */
51445             "headerchange": true,
51446         /**
51447              * @event hiddenchange
51448              * Fires when a column is hidden or "unhidden".
51449              * @param {ColumnModel} this
51450              * @param {Number} columnIndex The column index
51451              * @param {Boolean} hidden true if hidden, false otherwise
51452              */
51453             "hiddenchange": true,
51454             /**
51455          * @event columnmoved
51456          * Fires when a column is moved.
51457          * @param {ColumnModel} this
51458          * @param {Number} oldIndex
51459          * @param {Number} newIndex
51460          */
51461         "columnmoved" : true,
51462         /**
51463          * @event columlockchange
51464          * Fires when a column's locked state is changed
51465          * @param {ColumnModel} this
51466          * @param {Number} colIndex
51467          * @param {Boolean} locked true if locked
51468          */
51469         "columnlockchange" : true
51470     });
51471     Roo.grid.ColumnModel.superclass.constructor.call(this);
51472 };
51473 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
51474     /**
51475      * @cfg {String} header The header text to display in the Grid view.
51476      */
51477     /**
51478      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
51479      * {@link Roo.data.Record} definition from which to draw the column's value. If not
51480      * specified, the column's index is used as an index into the Record's data Array.
51481      */
51482     /**
51483      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
51484      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
51485      */
51486     /**
51487      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
51488      * Defaults to the value of the {@link #defaultSortable} property.
51489      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
51490      */
51491     /**
51492      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
51493      */
51494     /**
51495      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
51496      */
51497     /**
51498      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
51499      */
51500     /**
51501      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
51502      */
51503     /**
51504      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
51505      * given the cell's data value. See {@link #setRenderer}. If not specified, the
51506      * default renderer uses the raw data value.
51507      */
51508        /**
51509      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
51510      */
51511     /**
51512      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
51513      */
51514
51515     /**
51516      * Returns the id of the column at the specified index.
51517      * @param {Number} index The column index
51518      * @return {String} the id
51519      */
51520     getColumnId : function(index){
51521         return this.config[index].id;
51522     },
51523
51524     /**
51525      * Returns the column for a specified id.
51526      * @param {String} id The column id
51527      * @return {Object} the column
51528      */
51529     getColumnById : function(id){
51530         return this.lookup[id];
51531     },
51532
51533     
51534     /**
51535      * Returns the column for a specified dataIndex.
51536      * @param {String} dataIndex The column dataIndex
51537      * @return {Object|Boolean} the column or false if not found
51538      */
51539     getColumnByDataIndex: function(dataIndex){
51540         var index = this.findColumnIndex(dataIndex);
51541         return index > -1 ? this.config[index] : false;
51542     },
51543     
51544     /**
51545      * Returns the index for a specified column id.
51546      * @param {String} id The column id
51547      * @return {Number} the index, or -1 if not found
51548      */
51549     getIndexById : function(id){
51550         for(var i = 0, len = this.config.length; i < len; i++){
51551             if(this.config[i].id == id){
51552                 return i;
51553             }
51554         }
51555         return -1;
51556     },
51557     
51558     /**
51559      * Returns the index for a specified column dataIndex.
51560      * @param {String} dataIndex The column dataIndex
51561      * @return {Number} the index, or -1 if not found
51562      */
51563     
51564     findColumnIndex : function(dataIndex){
51565         for(var i = 0, len = this.config.length; i < len; i++){
51566             if(this.config[i].dataIndex == dataIndex){
51567                 return i;
51568             }
51569         }
51570         return -1;
51571     },
51572     
51573     
51574     moveColumn : function(oldIndex, newIndex){
51575         var c = this.config[oldIndex];
51576         this.config.splice(oldIndex, 1);
51577         this.config.splice(newIndex, 0, c);
51578         this.dataMap = null;
51579         this.fireEvent("columnmoved", this, oldIndex, newIndex);
51580     },
51581
51582     isLocked : function(colIndex){
51583         return this.config[colIndex].locked === true;
51584     },
51585
51586     setLocked : function(colIndex, value, suppressEvent){
51587         if(this.isLocked(colIndex) == value){
51588             return;
51589         }
51590         this.config[colIndex].locked = value;
51591         if(!suppressEvent){
51592             this.fireEvent("columnlockchange", this, colIndex, value);
51593         }
51594     },
51595
51596     getTotalLockedWidth : function(){
51597         var totalWidth = 0;
51598         for(var i = 0; i < this.config.length; i++){
51599             if(this.isLocked(i) && !this.isHidden(i)){
51600                 this.totalWidth += this.getColumnWidth(i);
51601             }
51602         }
51603         return totalWidth;
51604     },
51605
51606     getLockedCount : function(){
51607         for(var i = 0, len = this.config.length; i < len; i++){
51608             if(!this.isLocked(i)){
51609                 return i;
51610             }
51611         }
51612     },
51613
51614     /**
51615      * Returns the number of columns.
51616      * @return {Number}
51617      */
51618     getColumnCount : function(visibleOnly){
51619         if(visibleOnly === true){
51620             var c = 0;
51621             for(var i = 0, len = this.config.length; i < len; i++){
51622                 if(!this.isHidden(i)){
51623                     c++;
51624                 }
51625             }
51626             return c;
51627         }
51628         return this.config.length;
51629     },
51630
51631     /**
51632      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
51633      * @param {Function} fn
51634      * @param {Object} scope (optional)
51635      * @return {Array} result
51636      */
51637     getColumnsBy : function(fn, scope){
51638         var r = [];
51639         for(var i = 0, len = this.config.length; i < len; i++){
51640             var c = this.config[i];
51641             if(fn.call(scope||this, c, i) === true){
51642                 r[r.length] = c;
51643             }
51644         }
51645         return r;
51646     },
51647
51648     /**
51649      * Returns true if the specified column is sortable.
51650      * @param {Number} col The column index
51651      * @return {Boolean}
51652      */
51653     isSortable : function(col){
51654         if(typeof this.config[col].sortable == "undefined"){
51655             return this.defaultSortable;
51656         }
51657         return this.config[col].sortable;
51658     },
51659
51660     /**
51661      * Returns the rendering (formatting) function defined for the column.
51662      * @param {Number} col The column index.
51663      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
51664      */
51665     getRenderer : function(col){
51666         if(!this.config[col].renderer){
51667             return Roo.grid.ColumnModel.defaultRenderer;
51668         }
51669         return this.config[col].renderer;
51670     },
51671
51672     /**
51673      * Sets the rendering (formatting) function for a column.
51674      * @param {Number} col The column index
51675      * @param {Function} fn The function to use to process the cell's raw data
51676      * to return HTML markup for the grid view. The render function is called with
51677      * the following parameters:<ul>
51678      * <li>Data value.</li>
51679      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
51680      * <li>css A CSS style string to apply to the table cell.</li>
51681      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
51682      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
51683      * <li>Row index</li>
51684      * <li>Column index</li>
51685      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
51686      */
51687     setRenderer : function(col, fn){
51688         this.config[col].renderer = fn;
51689     },
51690
51691     /**
51692      * Returns the width for the specified column.
51693      * @param {Number} col The column index
51694      * @return {Number}
51695      */
51696     getColumnWidth : function(col){
51697         return this.config[col].width * 1 || this.defaultWidth;
51698     },
51699
51700     /**
51701      * Sets the width for a column.
51702      * @param {Number} col The column index
51703      * @param {Number} width The new width
51704      */
51705     setColumnWidth : function(col, width, suppressEvent){
51706         this.config[col].width = width;
51707         this.totalWidth = null;
51708         if(!suppressEvent){
51709              this.fireEvent("widthchange", this, col, width);
51710         }
51711     },
51712
51713     /**
51714      * Returns the total width of all columns.
51715      * @param {Boolean} includeHidden True to include hidden column widths
51716      * @return {Number}
51717      */
51718     getTotalWidth : function(includeHidden){
51719         if(!this.totalWidth){
51720             this.totalWidth = 0;
51721             for(var i = 0, len = this.config.length; i < len; i++){
51722                 if(includeHidden || !this.isHidden(i)){
51723                     this.totalWidth += this.getColumnWidth(i);
51724                 }
51725             }
51726         }
51727         return this.totalWidth;
51728     },
51729
51730     /**
51731      * Returns the header for the specified column.
51732      * @param {Number} col The column index
51733      * @return {String}
51734      */
51735     getColumnHeader : function(col){
51736         return this.config[col].header;
51737     },
51738
51739     /**
51740      * Sets the header for a column.
51741      * @param {Number} col The column index
51742      * @param {String} header The new header
51743      */
51744     setColumnHeader : function(col, header){
51745         this.config[col].header = header;
51746         this.fireEvent("headerchange", this, col, header);
51747     },
51748
51749     /**
51750      * Returns the tooltip for the specified column.
51751      * @param {Number} col The column index
51752      * @return {String}
51753      */
51754     getColumnTooltip : function(col){
51755             return this.config[col].tooltip;
51756     },
51757     /**
51758      * Sets the tooltip for a column.
51759      * @param {Number} col The column index
51760      * @param {String} tooltip The new tooltip
51761      */
51762     setColumnTooltip : function(col, tooltip){
51763             this.config[col].tooltip = tooltip;
51764     },
51765
51766     /**
51767      * Returns the dataIndex for the specified column.
51768      * @param {Number} col The column index
51769      * @return {Number}
51770      */
51771     getDataIndex : function(col){
51772         return this.config[col].dataIndex;
51773     },
51774
51775     /**
51776      * Sets the dataIndex for a column.
51777      * @param {Number} col The column index
51778      * @param {Number} dataIndex The new dataIndex
51779      */
51780     setDataIndex : function(col, dataIndex){
51781         this.config[col].dataIndex = dataIndex;
51782     },
51783
51784     
51785     
51786     /**
51787      * Returns true if the cell is editable.
51788      * @param {Number} colIndex The column index
51789      * @param {Number} rowIndex The row index
51790      * @return {Boolean}
51791      */
51792     isCellEditable : function(colIndex, rowIndex){
51793         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
51794     },
51795
51796     /**
51797      * Returns the editor defined for the cell/column.
51798      * return false or null to disable editing.
51799      * @param {Number} colIndex The column index
51800      * @param {Number} rowIndex The row index
51801      * @return {Object}
51802      */
51803     getCellEditor : function(colIndex, rowIndex){
51804         return this.config[colIndex].editor;
51805     },
51806
51807     /**
51808      * Sets if a column is editable.
51809      * @param {Number} col The column index
51810      * @param {Boolean} editable True if the column is editable
51811      */
51812     setEditable : function(col, editable){
51813         this.config[col].editable = editable;
51814     },
51815
51816
51817     /**
51818      * Returns true if the column is hidden.
51819      * @param {Number} colIndex The column index
51820      * @return {Boolean}
51821      */
51822     isHidden : function(colIndex){
51823         return this.config[colIndex].hidden;
51824     },
51825
51826
51827     /**
51828      * Returns true if the column width cannot be changed
51829      */
51830     isFixed : function(colIndex){
51831         return this.config[colIndex].fixed;
51832     },
51833
51834     /**
51835      * Returns true if the column can be resized
51836      * @return {Boolean}
51837      */
51838     isResizable : function(colIndex){
51839         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
51840     },
51841     /**
51842      * Sets if a column is hidden.
51843      * @param {Number} colIndex The column index
51844      * @param {Boolean} hidden True if the column is hidden
51845      */
51846     setHidden : function(colIndex, hidden){
51847         this.config[colIndex].hidden = hidden;
51848         this.totalWidth = null;
51849         this.fireEvent("hiddenchange", this, colIndex, hidden);
51850     },
51851
51852     /**
51853      * Sets the editor for a column.
51854      * @param {Number} col The column index
51855      * @param {Object} editor The editor object
51856      */
51857     setEditor : function(col, editor){
51858         this.config[col].editor = editor;
51859     }
51860 });
51861
51862 Roo.grid.ColumnModel.defaultRenderer = function(value){
51863         if(typeof value == "string" && value.length < 1){
51864             return "&#160;";
51865         }
51866         return value;
51867 };
51868
51869 // Alias for backwards compatibility
51870 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
51871 /*
51872  * Based on:
51873  * Ext JS Library 1.1.1
51874  * Copyright(c) 2006-2007, Ext JS, LLC.
51875  *
51876  * Originally Released Under LGPL - original licence link has changed is not relivant.
51877  *
51878  * Fork - LGPL
51879  * <script type="text/javascript">
51880  */
51881
51882 /**
51883  * @class Roo.grid.AbstractSelectionModel
51884  * @extends Roo.util.Observable
51885  * Abstract base class for grid SelectionModels.  It provides the interface that should be
51886  * implemented by descendant classes.  This class should not be directly instantiated.
51887  * @constructor
51888  */
51889 Roo.grid.AbstractSelectionModel = function(){
51890     this.locked = false;
51891     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
51892 };
51893
51894 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
51895     /** @ignore Called by the grid automatically. Do not call directly. */
51896     init : function(grid){
51897         this.grid = grid;
51898         this.initEvents();
51899     },
51900
51901     /**
51902      * Locks the selections.
51903      */
51904     lock : function(){
51905         this.locked = true;
51906     },
51907
51908     /**
51909      * Unlocks the selections.
51910      */
51911     unlock : function(){
51912         this.locked = false;
51913     },
51914
51915     /**
51916      * Returns true if the selections are locked.
51917      * @return {Boolean}
51918      */
51919     isLocked : function(){
51920         return this.locked;
51921     }
51922 });/*
51923  * Based on:
51924  * Ext JS Library 1.1.1
51925  * Copyright(c) 2006-2007, Ext JS, LLC.
51926  *
51927  * Originally Released Under LGPL - original licence link has changed is not relivant.
51928  *
51929  * Fork - LGPL
51930  * <script type="text/javascript">
51931  */
51932 /**
51933  * @extends Roo.grid.AbstractSelectionModel
51934  * @class Roo.grid.RowSelectionModel
51935  * The default SelectionModel used by {@link Roo.grid.Grid}.
51936  * It supports multiple selections and keyboard selection/navigation. 
51937  * @constructor
51938  * @param {Object} config
51939  */
51940 Roo.grid.RowSelectionModel = function(config){
51941     Roo.apply(this, config);
51942     this.selections = new Roo.util.MixedCollection(false, function(o){
51943         return o.id;
51944     });
51945
51946     this.last = false;
51947     this.lastActive = false;
51948
51949     this.addEvents({
51950         /**
51951              * @event selectionchange
51952              * Fires when the selection changes
51953              * @param {SelectionModel} this
51954              */
51955             "selectionchange" : true,
51956         /**
51957              * @event afterselectionchange
51958              * Fires after the selection changes (eg. by key press or clicking)
51959              * @param {SelectionModel} this
51960              */
51961             "afterselectionchange" : true,
51962         /**
51963              * @event beforerowselect
51964              * Fires when a row is selected being selected, return false to cancel.
51965              * @param {SelectionModel} this
51966              * @param {Number} rowIndex The selected index
51967              * @param {Boolean} keepExisting False if other selections will be cleared
51968              */
51969             "beforerowselect" : true,
51970         /**
51971              * @event rowselect
51972              * Fires when a row is selected.
51973              * @param {SelectionModel} this
51974              * @param {Number} rowIndex The selected index
51975              * @param {Roo.data.Record} r The record
51976              */
51977             "rowselect" : true,
51978         /**
51979              * @event rowdeselect
51980              * Fires when a row is deselected.
51981              * @param {SelectionModel} this
51982              * @param {Number} rowIndex The selected index
51983              */
51984         "rowdeselect" : true
51985     });
51986     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
51987     this.locked = false;
51988 };
51989
51990 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
51991     /**
51992      * @cfg {Boolean} singleSelect
51993      * True to allow selection of only one row at a time (defaults to false)
51994      */
51995     singleSelect : false,
51996
51997     // private
51998     initEvents : function(){
51999
52000         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
52001             this.grid.on("mousedown", this.handleMouseDown, this);
52002         }else{ // allow click to work like normal
52003             this.grid.on("rowclick", this.handleDragableRowClick, this);
52004         }
52005
52006         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
52007             "up" : function(e){
52008                 if(!e.shiftKey){
52009                     this.selectPrevious(e.shiftKey);
52010                 }else if(this.last !== false && this.lastActive !== false){
52011                     var last = this.last;
52012                     this.selectRange(this.last,  this.lastActive-1);
52013                     this.grid.getView().focusRow(this.lastActive);
52014                     if(last !== false){
52015                         this.last = last;
52016                     }
52017                 }else{
52018                     this.selectFirstRow();
52019                 }
52020                 this.fireEvent("afterselectionchange", this);
52021             },
52022             "down" : function(e){
52023                 if(!e.shiftKey){
52024                     this.selectNext(e.shiftKey);
52025                 }else if(this.last !== false && this.lastActive !== false){
52026                     var last = this.last;
52027                     this.selectRange(this.last,  this.lastActive+1);
52028                     this.grid.getView().focusRow(this.lastActive);
52029                     if(last !== false){
52030                         this.last = last;
52031                     }
52032                 }else{
52033                     this.selectFirstRow();
52034                 }
52035                 this.fireEvent("afterselectionchange", this);
52036             },
52037             scope: this
52038         });
52039
52040         var view = this.grid.view;
52041         view.on("refresh", this.onRefresh, this);
52042         view.on("rowupdated", this.onRowUpdated, this);
52043         view.on("rowremoved", this.onRemove, this);
52044     },
52045
52046     // private
52047     onRefresh : function(){
52048         var ds = this.grid.dataSource, i, v = this.grid.view;
52049         var s = this.selections;
52050         s.each(function(r){
52051             if((i = ds.indexOfId(r.id)) != -1){
52052                 v.onRowSelect(i);
52053             }else{
52054                 s.remove(r);
52055             }
52056         });
52057     },
52058
52059     // private
52060     onRemove : function(v, index, r){
52061         this.selections.remove(r);
52062     },
52063
52064     // private
52065     onRowUpdated : function(v, index, r){
52066         if(this.isSelected(r)){
52067             v.onRowSelect(index);
52068         }
52069     },
52070
52071     /**
52072      * Select records.
52073      * @param {Array} records The records to select
52074      * @param {Boolean} keepExisting (optional) True to keep existing selections
52075      */
52076     selectRecords : function(records, keepExisting){
52077         if(!keepExisting){
52078             this.clearSelections();
52079         }
52080         var ds = this.grid.dataSource;
52081         for(var i = 0, len = records.length; i < len; i++){
52082             this.selectRow(ds.indexOf(records[i]), true);
52083         }
52084     },
52085
52086     /**
52087      * Gets the number of selected rows.
52088      * @return {Number}
52089      */
52090     getCount : function(){
52091         return this.selections.length;
52092     },
52093
52094     /**
52095      * Selects the first row in the grid.
52096      */
52097     selectFirstRow : function(){
52098         this.selectRow(0);
52099     },
52100
52101     /**
52102      * Select the last row.
52103      * @param {Boolean} keepExisting (optional) True to keep existing selections
52104      */
52105     selectLastRow : function(keepExisting){
52106         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
52107     },
52108
52109     /**
52110      * Selects the row immediately following the last selected row.
52111      * @param {Boolean} keepExisting (optional) True to keep existing selections
52112      */
52113     selectNext : function(keepExisting){
52114         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
52115             this.selectRow(this.last+1, keepExisting);
52116             this.grid.getView().focusRow(this.last);
52117         }
52118     },
52119
52120     /**
52121      * Selects the row that precedes the last selected row.
52122      * @param {Boolean} keepExisting (optional) True to keep existing selections
52123      */
52124     selectPrevious : function(keepExisting){
52125         if(this.last){
52126             this.selectRow(this.last-1, keepExisting);
52127             this.grid.getView().focusRow(this.last);
52128         }
52129     },
52130
52131     /**
52132      * Returns the selected records
52133      * @return {Array} Array of selected records
52134      */
52135     getSelections : function(){
52136         return [].concat(this.selections.items);
52137     },
52138
52139     /**
52140      * Returns the first selected record.
52141      * @return {Record}
52142      */
52143     getSelected : function(){
52144         return this.selections.itemAt(0);
52145     },
52146
52147
52148     /**
52149      * Clears all selections.
52150      */
52151     clearSelections : function(fast){
52152         if(this.locked) return;
52153         if(fast !== true){
52154             var ds = this.grid.dataSource;
52155             var s = this.selections;
52156             s.each(function(r){
52157                 this.deselectRow(ds.indexOfId(r.id));
52158             }, this);
52159             s.clear();
52160         }else{
52161             this.selections.clear();
52162         }
52163         this.last = false;
52164     },
52165
52166
52167     /**
52168      * Selects all rows.
52169      */
52170     selectAll : function(){
52171         if(this.locked) return;
52172         this.selections.clear();
52173         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
52174             this.selectRow(i, true);
52175         }
52176     },
52177
52178     /**
52179      * Returns True if there is a selection.
52180      * @return {Boolean}
52181      */
52182     hasSelection : function(){
52183         return this.selections.length > 0;
52184     },
52185
52186     /**
52187      * Returns True if the specified row is selected.
52188      * @param {Number/Record} record The record or index of the record to check
52189      * @return {Boolean}
52190      */
52191     isSelected : function(index){
52192         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
52193         return (r && this.selections.key(r.id) ? true : false);
52194     },
52195
52196     /**
52197      * Returns True if the specified record id is selected.
52198      * @param {String} id The id of record to check
52199      * @return {Boolean}
52200      */
52201     isIdSelected : function(id){
52202         return (this.selections.key(id) ? true : false);
52203     },
52204
52205     // private
52206     handleMouseDown : function(e, t){
52207         var view = this.grid.getView(), rowIndex;
52208         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
52209             return;
52210         };
52211         if(e.shiftKey && this.last !== false){
52212             var last = this.last;
52213             this.selectRange(last, rowIndex, e.ctrlKey);
52214             this.last = last; // reset the last
52215             view.focusRow(rowIndex);
52216         }else{
52217             var isSelected = this.isSelected(rowIndex);
52218             if(e.button !== 0 && isSelected){
52219                 view.focusRow(rowIndex);
52220             }else if(e.ctrlKey && isSelected){
52221                 this.deselectRow(rowIndex);
52222             }else if(!isSelected){
52223                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
52224                 view.focusRow(rowIndex);
52225             }
52226         }
52227         this.fireEvent("afterselectionchange", this);
52228     },
52229     // private
52230     handleDragableRowClick :  function(grid, rowIndex, e) 
52231     {
52232         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
52233             this.selectRow(rowIndex, false);
52234             grid.view.focusRow(rowIndex);
52235              this.fireEvent("afterselectionchange", this);
52236         }
52237     },
52238     
52239     /**
52240      * Selects multiple rows.
52241      * @param {Array} rows Array of the indexes of the row to select
52242      * @param {Boolean} keepExisting (optional) True to keep existing selections
52243      */
52244     selectRows : function(rows, keepExisting){
52245         if(!keepExisting){
52246             this.clearSelections();
52247         }
52248         for(var i = 0, len = rows.length; i < len; i++){
52249             this.selectRow(rows[i], true);
52250         }
52251     },
52252
52253     /**
52254      * Selects a range of rows. All rows in between startRow and endRow are also selected.
52255      * @param {Number} startRow The index of the first row in the range
52256      * @param {Number} endRow The index of the last row in the range
52257      * @param {Boolean} keepExisting (optional) True to retain existing selections
52258      */
52259     selectRange : function(startRow, endRow, keepExisting){
52260         if(this.locked) return;
52261         if(!keepExisting){
52262             this.clearSelections();
52263         }
52264         if(startRow <= endRow){
52265             for(var i = startRow; i <= endRow; i++){
52266                 this.selectRow(i, true);
52267             }
52268         }else{
52269             for(var i = startRow; i >= endRow; i--){
52270                 this.selectRow(i, true);
52271             }
52272         }
52273     },
52274
52275     /**
52276      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
52277      * @param {Number} startRow The index of the first row in the range
52278      * @param {Number} endRow The index of the last row in the range
52279      */
52280     deselectRange : function(startRow, endRow, preventViewNotify){
52281         if(this.locked) return;
52282         for(var i = startRow; i <= endRow; i++){
52283             this.deselectRow(i, preventViewNotify);
52284         }
52285     },
52286
52287     /**
52288      * Selects a row.
52289      * @param {Number} row The index of the row to select
52290      * @param {Boolean} keepExisting (optional) True to keep existing selections
52291      */
52292     selectRow : function(index, keepExisting, preventViewNotify){
52293         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
52294         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
52295             if(!keepExisting || this.singleSelect){
52296                 this.clearSelections();
52297             }
52298             var r = this.grid.dataSource.getAt(index);
52299             this.selections.add(r);
52300             this.last = this.lastActive = index;
52301             if(!preventViewNotify){
52302                 this.grid.getView().onRowSelect(index);
52303             }
52304             this.fireEvent("rowselect", this, index, r);
52305             this.fireEvent("selectionchange", this);
52306         }
52307     },
52308
52309     /**
52310      * Deselects a row.
52311      * @param {Number} row The index of the row to deselect
52312      */
52313     deselectRow : function(index, preventViewNotify){
52314         if(this.locked) return;
52315         if(this.last == index){
52316             this.last = false;
52317         }
52318         if(this.lastActive == index){
52319             this.lastActive = false;
52320         }
52321         var r = this.grid.dataSource.getAt(index);
52322         this.selections.remove(r);
52323         if(!preventViewNotify){
52324             this.grid.getView().onRowDeselect(index);
52325         }
52326         this.fireEvent("rowdeselect", this, index);
52327         this.fireEvent("selectionchange", this);
52328     },
52329
52330     // private
52331     restoreLast : function(){
52332         if(this._last){
52333             this.last = this._last;
52334         }
52335     },
52336
52337     // private
52338     acceptsNav : function(row, col, cm){
52339         return !cm.isHidden(col) && cm.isCellEditable(col, row);
52340     },
52341
52342     // private
52343     onEditorKey : function(field, e){
52344         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
52345         if(k == e.TAB){
52346             e.stopEvent();
52347             ed.completeEdit();
52348             if(e.shiftKey){
52349                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
52350             }else{
52351                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
52352             }
52353         }else if(k == e.ENTER && !e.ctrlKey){
52354             e.stopEvent();
52355             ed.completeEdit();
52356             if(e.shiftKey){
52357                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
52358             }else{
52359                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
52360             }
52361         }else if(k == e.ESC){
52362             ed.cancelEdit();
52363         }
52364         if(newCell){
52365             g.startEditing(newCell[0], newCell[1]);
52366         }
52367     }
52368 });/*
52369  * Based on:
52370  * Ext JS Library 1.1.1
52371  * Copyright(c) 2006-2007, Ext JS, LLC.
52372  *
52373  * Originally Released Under LGPL - original licence link has changed is not relivant.
52374  *
52375  * Fork - LGPL
52376  * <script type="text/javascript">
52377  */
52378 /**
52379  * @class Roo.grid.CellSelectionModel
52380  * @extends Roo.grid.AbstractSelectionModel
52381  * This class provides the basic implementation for cell selection in a grid.
52382  * @constructor
52383  * @param {Object} config The object containing the configuration of this model.
52384  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
52385  */
52386 Roo.grid.CellSelectionModel = function(config){
52387     Roo.apply(this, config);
52388
52389     this.selection = null;
52390
52391     this.addEvents({
52392         /**
52393              * @event beforerowselect
52394              * Fires before a cell is selected.
52395              * @param {SelectionModel} this
52396              * @param {Number} rowIndex The selected row index
52397              * @param {Number} colIndex The selected cell index
52398              */
52399             "beforecellselect" : true,
52400         /**
52401              * @event cellselect
52402              * Fires when a cell is selected.
52403              * @param {SelectionModel} this
52404              * @param {Number} rowIndex The selected row index
52405              * @param {Number} colIndex The selected cell index
52406              */
52407             "cellselect" : true,
52408         /**
52409              * @event selectionchange
52410              * Fires when the active selection changes.
52411              * @param {SelectionModel} this
52412              * @param {Object} selection null for no selection or an object (o) with two properties
52413                 <ul>
52414                 <li>o.record: the record object for the row the selection is in</li>
52415                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
52416                 </ul>
52417              */
52418             "selectionchange" : true,
52419         /**
52420              * @event tabend
52421              * Fires when the tab (or enter) was pressed on the last editable cell
52422              * You can use this to trigger add new row.
52423              * @param {SelectionModel} this
52424              */
52425             "tabend" : true,
52426          /**
52427              * @event beforeeditnext
52428              * Fires before the next editable sell is made active
52429              * You can use this to skip to another cell or fire the tabend
52430              *    if you set cell to false
52431              * @param {Object} eventdata object : { cell : [ row, col ] } 
52432              */
52433             "beforeeditnext" : true
52434     });
52435     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
52436 };
52437
52438 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
52439     
52440     enter_is_tab: false,
52441
52442     /** @ignore */
52443     initEvents : function(){
52444         this.grid.on("mousedown", this.handleMouseDown, this);
52445         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
52446         var view = this.grid.view;
52447         view.on("refresh", this.onViewChange, this);
52448         view.on("rowupdated", this.onRowUpdated, this);
52449         view.on("beforerowremoved", this.clearSelections, this);
52450         view.on("beforerowsinserted", this.clearSelections, this);
52451         if(this.grid.isEditor){
52452             this.grid.on("beforeedit", this.beforeEdit,  this);
52453         }
52454     },
52455
52456         //private
52457     beforeEdit : function(e){
52458         this.select(e.row, e.column, false, true, e.record);
52459     },
52460
52461         //private
52462     onRowUpdated : function(v, index, r){
52463         if(this.selection && this.selection.record == r){
52464             v.onCellSelect(index, this.selection.cell[1]);
52465         }
52466     },
52467
52468         //private
52469     onViewChange : function(){
52470         this.clearSelections(true);
52471     },
52472
52473         /**
52474          * Returns the currently selected cell,.
52475          * @return {Array} The selected cell (row, column) or null if none selected.
52476          */
52477     getSelectedCell : function(){
52478         return this.selection ? this.selection.cell : null;
52479     },
52480
52481     /**
52482      * Clears all selections.
52483      * @param {Boolean} true to prevent the gridview from being notified about the change.
52484      */
52485     clearSelections : function(preventNotify){
52486         var s = this.selection;
52487         if(s){
52488             if(preventNotify !== true){
52489                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
52490             }
52491             this.selection = null;
52492             this.fireEvent("selectionchange", this, null);
52493         }
52494     },
52495
52496     /**
52497      * Returns true if there is a selection.
52498      * @return {Boolean}
52499      */
52500     hasSelection : function(){
52501         return this.selection ? true : false;
52502     },
52503
52504     /** @ignore */
52505     handleMouseDown : function(e, t){
52506         var v = this.grid.getView();
52507         if(this.isLocked()){
52508             return;
52509         };
52510         var row = v.findRowIndex(t);
52511         var cell = v.findCellIndex(t);
52512         if(row !== false && cell !== false){
52513             this.select(row, cell);
52514         }
52515     },
52516
52517     /**
52518      * Selects a cell.
52519      * @param {Number} rowIndex
52520      * @param {Number} collIndex
52521      */
52522     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
52523         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
52524             this.clearSelections();
52525             r = r || this.grid.dataSource.getAt(rowIndex);
52526             this.selection = {
52527                 record : r,
52528                 cell : [rowIndex, colIndex]
52529             };
52530             if(!preventViewNotify){
52531                 var v = this.grid.getView();
52532                 v.onCellSelect(rowIndex, colIndex);
52533                 if(preventFocus !== true){
52534                     v.focusCell(rowIndex, colIndex);
52535                 }
52536             }
52537             this.fireEvent("cellselect", this, rowIndex, colIndex);
52538             this.fireEvent("selectionchange", this, this.selection);
52539         }
52540     },
52541
52542         //private
52543     isSelectable : function(rowIndex, colIndex, cm){
52544         return !cm.isHidden(colIndex);
52545     },
52546
52547     /** @ignore */
52548     handleKeyDown : function(e){
52549         //Roo.log('Cell Sel Model handleKeyDown');
52550         if(!e.isNavKeyPress()){
52551             return;
52552         }
52553         var g = this.grid, s = this.selection;
52554         if(!s){
52555             e.stopEvent();
52556             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
52557             if(cell){
52558                 this.select(cell[0], cell[1]);
52559             }
52560             return;
52561         }
52562         var sm = this;
52563         var walk = function(row, col, step){
52564             return g.walkCells(row, col, step, sm.isSelectable,  sm);
52565         };
52566         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
52567         var newCell;
52568
52569       
52570
52571         switch(k){
52572             case e.TAB:
52573                 // handled by onEditorKey
52574                 if (g.isEditor && g.editing) {
52575                     return;
52576                 }
52577                 if(e.shiftKey) {
52578                     newCell = walk(r, c-1, -1);
52579                 } else {
52580                     newCell = walk(r, c+1, 1);
52581                 }
52582                 break;
52583             
52584             case e.DOWN:
52585                newCell = walk(r+1, c, 1);
52586                 break;
52587             
52588             case e.UP:
52589                 newCell = walk(r-1, c, -1);
52590                 break;
52591             
52592             case e.RIGHT:
52593                 newCell = walk(r, c+1, 1);
52594                 break;
52595             
52596             case e.LEFT:
52597                 newCell = walk(r, c-1, -1);
52598                 break;
52599             
52600             case e.ENTER:
52601                 
52602                 if(g.isEditor && !g.editing){
52603                    g.startEditing(r, c);
52604                    e.stopEvent();
52605                    return;
52606                 }
52607                 
52608                 
52609              break;
52610         };
52611         if(newCell){
52612             this.select(newCell[0], newCell[1]);
52613             e.stopEvent();
52614             
52615         }
52616     },
52617
52618     acceptsNav : function(row, col, cm){
52619         return !cm.isHidden(col) && cm.isCellEditable(col, row);
52620     },
52621     /**
52622      * Selects a cell.
52623      * @param {Number} field (not used) - as it's normally used as a listener
52624      * @param {Number} e - event - fake it by using
52625      *
52626      * var e = Roo.EventObjectImpl.prototype;
52627      * e.keyCode = e.TAB
52628      *
52629      * 
52630      */
52631     onEditorKey : function(field, e){
52632         
52633         var k = e.getKey(),
52634             newCell,
52635             g = this.grid,
52636             ed = g.activeEditor,
52637             forward = false;
52638         ///Roo.log('onEditorKey' + k);
52639         
52640         
52641         if (this.enter_is_tab && k == e.ENTER) {
52642             k = e.TAB;
52643         }
52644         
52645         if(k == e.TAB){
52646             if(e.shiftKey){
52647                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
52648             }else{
52649                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
52650                 forward = true;
52651             }
52652             
52653             e.stopEvent();
52654             
52655         } else if(k == e.ENTER &&  !e.ctrlKey){
52656             ed.completeEdit();
52657             e.stopEvent();
52658             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
52659         
52660                 } else if(k == e.ESC){
52661             ed.cancelEdit();
52662         }
52663                 
52664         if (newCell) {
52665             var ecall = { cell : newCell, forward : forward };
52666             this.fireEvent('beforeeditnext', ecall );
52667             newCell = ecall.cell;
52668                         forward = ecall.forward;
52669         }
52670                 
52671         if(newCell){
52672             //Roo.log('next cell after edit');
52673             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
52674         } else if (forward) {
52675             // tabbed past last
52676             this.fireEvent.defer(100, this, ['tabend',this]);
52677         }
52678     }
52679 });/*
52680  * Based on:
52681  * Ext JS Library 1.1.1
52682  * Copyright(c) 2006-2007, Ext JS, LLC.
52683  *
52684  * Originally Released Under LGPL - original licence link has changed is not relivant.
52685  *
52686  * Fork - LGPL
52687  * <script type="text/javascript">
52688  */
52689  
52690 /**
52691  * @class Roo.grid.EditorGrid
52692  * @extends Roo.grid.Grid
52693  * Class for creating and editable grid.
52694  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
52695  * The container MUST have some type of size defined for the grid to fill. The container will be 
52696  * automatically set to position relative if it isn't already.
52697  * @param {Object} dataSource The data model to bind to
52698  * @param {Object} colModel The column model with info about this grid's columns
52699  */
52700 Roo.grid.EditorGrid = function(container, config){
52701     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
52702     this.getGridEl().addClass("xedit-grid");
52703
52704     if(!this.selModel){
52705         this.selModel = new Roo.grid.CellSelectionModel();
52706     }
52707
52708     this.activeEditor = null;
52709
52710         this.addEvents({
52711             /**
52712              * @event beforeedit
52713              * Fires before cell editing is triggered. The edit event object has the following properties <br />
52714              * <ul style="padding:5px;padding-left:16px;">
52715              * <li>grid - This grid</li>
52716              * <li>record - The record being edited</li>
52717              * <li>field - The field name being edited</li>
52718              * <li>value - The value for the field being edited.</li>
52719              * <li>row - The grid row index</li>
52720              * <li>column - The grid column index</li>
52721              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
52722              * </ul>
52723              * @param {Object} e An edit event (see above for description)
52724              */
52725             "beforeedit" : true,
52726             /**
52727              * @event afteredit
52728              * Fires after a cell is edited. <br />
52729              * <ul style="padding:5px;padding-left:16px;">
52730              * <li>grid - This grid</li>
52731              * <li>record - The record being edited</li>
52732              * <li>field - The field name being edited</li>
52733              * <li>value - The value being set</li>
52734              * <li>originalValue - The original value for the field, before the edit.</li>
52735              * <li>row - The grid row index</li>
52736              * <li>column - The grid column index</li>
52737              * </ul>
52738              * @param {Object} e An edit event (see above for description)
52739              */
52740             "afteredit" : true,
52741             /**
52742              * @event validateedit
52743              * Fires after a cell is edited, but before the value is set in the record. 
52744          * You can use this to modify the value being set in the field, Return false
52745              * to cancel the change. The edit event object has the following properties <br />
52746              * <ul style="padding:5px;padding-left:16px;">
52747          * <li>editor - This editor</li>
52748              * <li>grid - This grid</li>
52749              * <li>record - The record being edited</li>
52750              * <li>field - The field name being edited</li>
52751              * <li>value - The value being set</li>
52752              * <li>originalValue - The original value for the field, before the edit.</li>
52753              * <li>row - The grid row index</li>
52754              * <li>column - The grid column index</li>
52755              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
52756              * </ul>
52757              * @param {Object} e An edit event (see above for description)
52758              */
52759             "validateedit" : true
52760         });
52761     this.on("bodyscroll", this.stopEditing,  this);
52762     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
52763 };
52764
52765 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
52766     /**
52767      * @cfg {Number} clicksToEdit
52768      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
52769      */
52770     clicksToEdit: 2,
52771
52772     // private
52773     isEditor : true,
52774     // private
52775     trackMouseOver: false, // causes very odd FF errors
52776
52777     onCellDblClick : function(g, row, col){
52778         this.startEditing(row, col);
52779     },
52780
52781     onEditComplete : function(ed, value, startValue){
52782         this.editing = false;
52783         this.activeEditor = null;
52784         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
52785         var r = ed.record;
52786         var field = this.colModel.getDataIndex(ed.col);
52787         var e = {
52788             grid: this,
52789             record: r,
52790             field: field,
52791             originalValue: startValue,
52792             value: value,
52793             row: ed.row,
52794             column: ed.col,
52795             cancel:false,
52796             editor: ed
52797         };
52798         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
52799         cell.show();
52800           
52801         if(String(value) !== String(startValue)){
52802             
52803             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
52804                 r.set(field, e.value);
52805                 // if we are dealing with a combo box..
52806                 // then we also set the 'name' colum to be the displayField
52807                 if (ed.field.displayField && ed.field.name) {
52808                     r.set(ed.field.name, ed.field.el.dom.value);
52809                 }
52810                 
52811                 delete e.cancel; //?? why!!!
52812                 this.fireEvent("afteredit", e);
52813             }
52814         } else {
52815             this.fireEvent("afteredit", e); // always fire it!
52816         }
52817         this.view.focusCell(ed.row, ed.col);
52818     },
52819
52820     /**
52821      * Starts editing the specified for the specified row/column
52822      * @param {Number} rowIndex
52823      * @param {Number} colIndex
52824      */
52825     startEditing : function(row, col){
52826         this.stopEditing();
52827         if(this.colModel.isCellEditable(col, row)){
52828             this.view.ensureVisible(row, col, true);
52829           
52830             var r = this.dataSource.getAt(row);
52831             var field = this.colModel.getDataIndex(col);
52832             var cell = Roo.get(this.view.getCell(row,col));
52833             var e = {
52834                 grid: this,
52835                 record: r,
52836                 field: field,
52837                 value: r.data[field],
52838                 row: row,
52839                 column: col,
52840                 cancel:false 
52841             };
52842             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
52843                 this.editing = true;
52844                 var ed = this.colModel.getCellEditor(col, row);
52845                 
52846                 if (!ed) {
52847                     return;
52848                 }
52849                 if(!ed.rendered){
52850                     ed.render(ed.parentEl || document.body);
52851                 }
52852                 ed.field.reset();
52853                
52854                 cell.hide();
52855                 
52856                 (function(){ // complex but required for focus issues in safari, ie and opera
52857                     ed.row = row;
52858                     ed.col = col;
52859                     ed.record = r;
52860                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
52861                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
52862                     this.activeEditor = ed;
52863                     var v = r.data[field];
52864                     ed.startEdit(this.view.getCell(row, col), v);
52865                     // combo's with 'displayField and name set
52866                     if (ed.field.displayField && ed.field.name) {
52867                         ed.field.el.dom.value = r.data[ed.field.name];
52868                     }
52869                     
52870                     
52871                 }).defer(50, this);
52872             }
52873         }
52874     },
52875         
52876     /**
52877      * Stops any active editing
52878      */
52879     stopEditing : function(){
52880         if(this.activeEditor){
52881             this.activeEditor.completeEdit();
52882         }
52883         this.activeEditor = null;
52884     }
52885 });/*
52886  * Based on:
52887  * Ext JS Library 1.1.1
52888  * Copyright(c) 2006-2007, Ext JS, LLC.
52889  *
52890  * Originally Released Under LGPL - original licence link has changed is not relivant.
52891  *
52892  * Fork - LGPL
52893  * <script type="text/javascript">
52894  */
52895
52896 // private - not really -- you end up using it !
52897 // This is a support class used internally by the Grid components
52898
52899 /**
52900  * @class Roo.grid.GridEditor
52901  * @extends Roo.Editor
52902  * Class for creating and editable grid elements.
52903  * @param {Object} config any settings (must include field)
52904  */
52905 Roo.grid.GridEditor = function(field, config){
52906     if (!config && field.field) {
52907         config = field;
52908         field = Roo.factory(config.field, Roo.form);
52909     }
52910     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
52911     field.monitorTab = false;
52912 };
52913
52914 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
52915     
52916     /**
52917      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
52918      */
52919     
52920     alignment: "tl-tl",
52921     autoSize: "width",
52922     hideEl : false,
52923     cls: "x-small-editor x-grid-editor",
52924     shim:false,
52925     shadow:"frame"
52926 });/*
52927  * Based on:
52928  * Ext JS Library 1.1.1
52929  * Copyright(c) 2006-2007, Ext JS, LLC.
52930  *
52931  * Originally Released Under LGPL - original licence link has changed is not relivant.
52932  *
52933  * Fork - LGPL
52934  * <script type="text/javascript">
52935  */
52936   
52937
52938   
52939 Roo.grid.PropertyRecord = Roo.data.Record.create([
52940     {name:'name',type:'string'},  'value'
52941 ]);
52942
52943
52944 Roo.grid.PropertyStore = function(grid, source){
52945     this.grid = grid;
52946     this.store = new Roo.data.Store({
52947         recordType : Roo.grid.PropertyRecord
52948     });
52949     this.store.on('update', this.onUpdate,  this);
52950     if(source){
52951         this.setSource(source);
52952     }
52953     Roo.grid.PropertyStore.superclass.constructor.call(this);
52954 };
52955
52956
52957
52958 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
52959     setSource : function(o){
52960         this.source = o;
52961         this.store.removeAll();
52962         var data = [];
52963         for(var k in o){
52964             if(this.isEditableValue(o[k])){
52965                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
52966             }
52967         }
52968         this.store.loadRecords({records: data}, {}, true);
52969     },
52970
52971     onUpdate : function(ds, record, type){
52972         if(type == Roo.data.Record.EDIT){
52973             var v = record.data['value'];
52974             var oldValue = record.modified['value'];
52975             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
52976                 this.source[record.id] = v;
52977                 record.commit();
52978                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
52979             }else{
52980                 record.reject();
52981             }
52982         }
52983     },
52984
52985     getProperty : function(row){
52986        return this.store.getAt(row);
52987     },
52988
52989     isEditableValue: function(val){
52990         if(val && val instanceof Date){
52991             return true;
52992         }else if(typeof val == 'object' || typeof val == 'function'){
52993             return false;
52994         }
52995         return true;
52996     },
52997
52998     setValue : function(prop, value){
52999         this.source[prop] = value;
53000         this.store.getById(prop).set('value', value);
53001     },
53002
53003     getSource : function(){
53004         return this.source;
53005     }
53006 });
53007
53008 Roo.grid.PropertyColumnModel = function(grid, store){
53009     this.grid = grid;
53010     var g = Roo.grid;
53011     g.PropertyColumnModel.superclass.constructor.call(this, [
53012         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
53013         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
53014     ]);
53015     this.store = store;
53016     this.bselect = Roo.DomHelper.append(document.body, {
53017         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
53018             {tag: 'option', value: 'true', html: 'true'},
53019             {tag: 'option', value: 'false', html: 'false'}
53020         ]
53021     });
53022     Roo.id(this.bselect);
53023     var f = Roo.form;
53024     this.editors = {
53025         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
53026         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
53027         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
53028         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
53029         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
53030     };
53031     this.renderCellDelegate = this.renderCell.createDelegate(this);
53032     this.renderPropDelegate = this.renderProp.createDelegate(this);
53033 };
53034
53035 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
53036     
53037     
53038     nameText : 'Name',
53039     valueText : 'Value',
53040     
53041     dateFormat : 'm/j/Y',
53042     
53043     
53044     renderDate : function(dateVal){
53045         return dateVal.dateFormat(this.dateFormat);
53046     },
53047
53048     renderBool : function(bVal){
53049         return bVal ? 'true' : 'false';
53050     },
53051
53052     isCellEditable : function(colIndex, rowIndex){
53053         return colIndex == 1;
53054     },
53055
53056     getRenderer : function(col){
53057         return col == 1 ?
53058             this.renderCellDelegate : this.renderPropDelegate;
53059     },
53060
53061     renderProp : function(v){
53062         return this.getPropertyName(v);
53063     },
53064
53065     renderCell : function(val){
53066         var rv = val;
53067         if(val instanceof Date){
53068             rv = this.renderDate(val);
53069         }else if(typeof val == 'boolean'){
53070             rv = this.renderBool(val);
53071         }
53072         return Roo.util.Format.htmlEncode(rv);
53073     },
53074
53075     getPropertyName : function(name){
53076         var pn = this.grid.propertyNames;
53077         return pn && pn[name] ? pn[name] : name;
53078     },
53079
53080     getCellEditor : function(colIndex, rowIndex){
53081         var p = this.store.getProperty(rowIndex);
53082         var n = p.data['name'], val = p.data['value'];
53083         
53084         if(typeof(this.grid.customEditors[n]) == 'string'){
53085             return this.editors[this.grid.customEditors[n]];
53086         }
53087         if(typeof(this.grid.customEditors[n]) != 'undefined'){
53088             return this.grid.customEditors[n];
53089         }
53090         if(val instanceof Date){
53091             return this.editors['date'];
53092         }else if(typeof val == 'number'){
53093             return this.editors['number'];
53094         }else if(typeof val == 'boolean'){
53095             return this.editors['boolean'];
53096         }else{
53097             return this.editors['string'];
53098         }
53099     }
53100 });
53101
53102 /**
53103  * @class Roo.grid.PropertyGrid
53104  * @extends Roo.grid.EditorGrid
53105  * This class represents the  interface of a component based property grid control.
53106  * <br><br>Usage:<pre><code>
53107  var grid = new Roo.grid.PropertyGrid("my-container-id", {
53108       
53109  });
53110  // set any options
53111  grid.render();
53112  * </code></pre>
53113   
53114  * @constructor
53115  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
53116  * The container MUST have some type of size defined for the grid to fill. The container will be
53117  * automatically set to position relative if it isn't already.
53118  * @param {Object} config A config object that sets properties on this grid.
53119  */
53120 Roo.grid.PropertyGrid = function(container, config){
53121     config = config || {};
53122     var store = new Roo.grid.PropertyStore(this);
53123     this.store = store;
53124     var cm = new Roo.grid.PropertyColumnModel(this, store);
53125     store.store.sort('name', 'ASC');
53126     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
53127         ds: store.store,
53128         cm: cm,
53129         enableColLock:false,
53130         enableColumnMove:false,
53131         stripeRows:false,
53132         trackMouseOver: false,
53133         clicksToEdit:1
53134     }, config));
53135     this.getGridEl().addClass('x-props-grid');
53136     this.lastEditRow = null;
53137     this.on('columnresize', this.onColumnResize, this);
53138     this.addEvents({
53139          /**
53140              * @event beforepropertychange
53141              * Fires before a property changes (return false to stop?)
53142              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
53143              * @param {String} id Record Id
53144              * @param {String} newval New Value
53145          * @param {String} oldval Old Value
53146              */
53147         "beforepropertychange": true,
53148         /**
53149              * @event propertychange
53150              * Fires after a property changes
53151              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
53152              * @param {String} id Record Id
53153              * @param {String} newval New Value
53154          * @param {String} oldval Old Value
53155              */
53156         "propertychange": true
53157     });
53158     this.customEditors = this.customEditors || {};
53159 };
53160 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
53161     
53162      /**
53163      * @cfg {Object} customEditors map of colnames=> custom editors.
53164      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
53165      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
53166      * false disables editing of the field.
53167          */
53168     
53169       /**
53170      * @cfg {Object} propertyNames map of property Names to their displayed value
53171          */
53172     
53173     render : function(){
53174         Roo.grid.PropertyGrid.superclass.render.call(this);
53175         this.autoSize.defer(100, this);
53176     },
53177
53178     autoSize : function(){
53179         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
53180         if(this.view){
53181             this.view.fitColumns();
53182         }
53183     },
53184
53185     onColumnResize : function(){
53186         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
53187         this.autoSize();
53188     },
53189     /**
53190      * Sets the data for the Grid
53191      * accepts a Key => Value object of all the elements avaiable.
53192      * @param {Object} data  to appear in grid.
53193      */
53194     setSource : function(source){
53195         this.store.setSource(source);
53196         //this.autoSize();
53197     },
53198     /**
53199      * Gets all the data from the grid.
53200      * @return {Object} data  data stored in grid
53201      */
53202     getSource : function(){
53203         return this.store.getSource();
53204     }
53205 });/*
53206  * Based on:
53207  * Ext JS Library 1.1.1
53208  * Copyright(c) 2006-2007, Ext JS, LLC.
53209  *
53210  * Originally Released Under LGPL - original licence link has changed is not relivant.
53211  *
53212  * Fork - LGPL
53213  * <script type="text/javascript">
53214  */
53215  
53216 /**
53217  * @class Roo.LoadMask
53218  * A simple utility class for generically masking elements while loading data.  If the element being masked has
53219  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
53220  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
53221  * element's UpdateManager load indicator and will be destroyed after the initial load.
53222  * @constructor
53223  * Create a new LoadMask
53224  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
53225  * @param {Object} config The config object
53226  */
53227 Roo.LoadMask = function(el, config){
53228     this.el = Roo.get(el);
53229     Roo.apply(this, config);
53230     if(this.store){
53231         this.store.on('beforeload', this.onBeforeLoad, this);
53232         this.store.on('load', this.onLoad, this);
53233         this.store.on('loadexception', this.onLoadException, this);
53234         this.removeMask = false;
53235     }else{
53236         var um = this.el.getUpdateManager();
53237         um.showLoadIndicator = false; // disable the default indicator
53238         um.on('beforeupdate', this.onBeforeLoad, this);
53239         um.on('update', this.onLoad, this);
53240         um.on('failure', this.onLoad, this);
53241         this.removeMask = true;
53242     }
53243 };
53244
53245 Roo.LoadMask.prototype = {
53246     /**
53247      * @cfg {Boolean} removeMask
53248      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
53249      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
53250      */
53251     /**
53252      * @cfg {String} msg
53253      * The text to display in a centered loading message box (defaults to 'Loading...')
53254      */
53255     msg : 'Loading...',
53256     /**
53257      * @cfg {String} msgCls
53258      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
53259      */
53260     msgCls : 'x-mask-loading',
53261
53262     /**
53263      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
53264      * @type Boolean
53265      */
53266     disabled: false,
53267
53268     /**
53269      * Disables the mask to prevent it from being displayed
53270      */
53271     disable : function(){
53272        this.disabled = true;
53273     },
53274
53275     /**
53276      * Enables the mask so that it can be displayed
53277      */
53278     enable : function(){
53279         this.disabled = false;
53280     },
53281     
53282     onLoadException : function()
53283     {
53284         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53285             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53286         }
53287         this.el.unmask(this.removeMask);
53288     },
53289     // private
53290     onLoad : function()
53291     {
53292         this.el.unmask(this.removeMask);
53293     },
53294
53295     // private
53296     onBeforeLoad : function(){
53297         if(!this.disabled){
53298             this.el.mask(this.msg, this.msgCls);
53299         }
53300     },
53301
53302     // private
53303     destroy : function(){
53304         if(this.store){
53305             this.store.un('beforeload', this.onBeforeLoad, this);
53306             this.store.un('load', this.onLoad, this);
53307             this.store.un('loadexception', this.onLoadException, this);
53308         }else{
53309             var um = this.el.getUpdateManager();
53310             um.un('beforeupdate', this.onBeforeLoad, this);
53311             um.un('update', this.onLoad, this);
53312             um.un('failure', this.onLoad, this);
53313         }
53314     }
53315 };/*
53316  * Based on:
53317  * Ext JS Library 1.1.1
53318  * Copyright(c) 2006-2007, Ext JS, LLC.
53319  *
53320  * Originally Released Under LGPL - original licence link has changed is not relivant.
53321  *
53322  * Fork - LGPL
53323  * <script type="text/javascript">
53324  */
53325
53326
53327 /**
53328  * @class Roo.XTemplate
53329  * @extends Roo.Template
53330  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
53331 <pre><code>
53332 var t = new Roo.XTemplate(
53333         '&lt;select name="{name}"&gt;',
53334                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
53335         '&lt;/select&gt;'
53336 );
53337  
53338 // then append, applying the master template values
53339  </code></pre>
53340  *
53341  * Supported features:
53342  *
53343  *  Tags:
53344
53345 <pre><code>
53346       {a_variable} - output encoded.
53347       {a_variable.format:("Y-m-d")} - call a method on the variable
53348       {a_variable:raw} - unencoded output
53349       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
53350       {a_variable:this.method_on_template(...)} - call a method on the template object.
53351  
53352 </code></pre>
53353  *  The tpl tag:
53354 <pre><code>
53355         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
53356         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
53357         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
53358         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
53359   
53360         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
53361         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
53362 </code></pre>
53363  *      
53364  */
53365 Roo.XTemplate = function()
53366 {
53367     Roo.XTemplate.superclass.constructor.apply(this, arguments);
53368     if (this.html) {
53369         this.compile();
53370     }
53371 };
53372
53373
53374 Roo.extend(Roo.XTemplate, Roo.Template, {
53375
53376     /**
53377      * The various sub templates
53378      */
53379     tpls : false,
53380     /**
53381      *
53382      * basic tag replacing syntax
53383      * WORD:WORD()
53384      *
53385      * // you can fake an object call by doing this
53386      *  x.t:(test,tesT) 
53387      * 
53388      */
53389     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
53390
53391     /**
53392      * compile the template
53393      *
53394      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
53395      *
53396      */
53397     compile: function()
53398     {
53399         var s = this.html;
53400      
53401         s = ['<tpl>', s, '</tpl>'].join('');
53402     
53403         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
53404             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
53405             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
53406             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
53407             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
53408             m,
53409             id     = 0,
53410             tpls   = [];
53411     
53412         while(true == !!(m = s.match(re))){
53413             var forMatch   = m[0].match(nameRe),
53414                 ifMatch   = m[0].match(ifRe),
53415                 execMatch   = m[0].match(execRe),
53416                 namedMatch   = m[0].match(namedRe),
53417                 
53418                 exp  = null, 
53419                 fn   = null,
53420                 exec = null,
53421                 name = forMatch && forMatch[1] ? forMatch[1] : '';
53422                 
53423             if (ifMatch) {
53424                 // if - puts fn into test..
53425                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
53426                 if(exp){
53427                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
53428                 }
53429             }
53430             
53431             if (execMatch) {
53432                 // exec - calls a function... returns empty if true is  returned.
53433                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
53434                 if(exp){
53435                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
53436                 }
53437             }
53438             
53439             
53440             if (name) {
53441                 // for = 
53442                 switch(name){
53443                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
53444                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
53445                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
53446                 }
53447             }
53448             var uid = namedMatch ? namedMatch[1] : id;
53449             
53450             
53451             tpls.push({
53452                 id:     namedMatch ? namedMatch[1] : id,
53453                 target: name,
53454                 exec:   exec,
53455                 test:   fn,
53456                 body:   m[1] || ''
53457             });
53458             if (namedMatch) {
53459                 s = s.replace(m[0], '');
53460             } else { 
53461                 s = s.replace(m[0], '{xtpl'+ id + '}');
53462             }
53463             ++id;
53464         }
53465         this.tpls = [];
53466         for(var i = tpls.length-1; i >= 0; --i){
53467             this.compileTpl(tpls[i]);
53468             this.tpls[tpls[i].id] = tpls[i];
53469         }
53470         this.master = tpls[tpls.length-1];
53471         return this;
53472     },
53473     /**
53474      * same as applyTemplate, except it's done to one of the subTemplates
53475      * when using named templates, you can do:
53476      *
53477      * var str = pl.applySubTemplate('your-name', values);
53478      *
53479      * 
53480      * @param {Number} id of the template
53481      * @param {Object} values to apply to template
53482      * @param {Object} parent (normaly the instance of this object)
53483      */
53484     applySubTemplate : function(id, values, parent)
53485     {
53486         
53487         
53488         var t = this.tpls[id];
53489         
53490         
53491         try { 
53492             if(t.test && !t.test.call(this, values, parent)){
53493                 return '';
53494             }
53495         } catch(e) {
53496             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
53497             Roo.log(e.toString());
53498             Roo.log(t.test);
53499             return ''
53500         }
53501         try { 
53502             
53503             if(t.exec && t.exec.call(this, values, parent)){
53504                 return '';
53505             }
53506         } catch(e) {
53507             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
53508             Roo.log(e.toString());
53509             Roo.log(t.exec);
53510             return ''
53511         }
53512         try {
53513             var vs = t.target ? t.target.call(this, values, parent) : values;
53514             parent = t.target ? values : parent;
53515             if(t.target && vs instanceof Array){
53516                 var buf = [];
53517                 for(var i = 0, len = vs.length; i < len; i++){
53518                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
53519                 }
53520                 return buf.join('');
53521             }
53522             return t.compiled.call(this, vs, parent);
53523         } catch (e) {
53524             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
53525             Roo.log(e.toString());
53526             Roo.log(t.compiled);
53527             return '';
53528         }
53529     },
53530
53531     compileTpl : function(tpl)
53532     {
53533         var fm = Roo.util.Format;
53534         var useF = this.disableFormats !== true;
53535         var sep = Roo.isGecko ? "+" : ",";
53536         var undef = function(str) {
53537             Roo.log("Property not found :"  + str);
53538             return '';
53539         };
53540         
53541         var fn = function(m, name, format, args)
53542         {
53543             //Roo.log(arguments);
53544             args = args ? args.replace(/\\'/g,"'") : args;
53545             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
53546             if (typeof(format) == 'undefined') {
53547                 format= 'htmlEncode';
53548             }
53549             if (format == 'raw' ) {
53550                 format = false;
53551             }
53552             
53553             if(name.substr(0, 4) == 'xtpl'){
53554                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
53555             }
53556             
53557             // build an array of options to determine if value is undefined..
53558             
53559             // basically get 'xxxx.yyyy' then do
53560             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
53561             //    (function () { Roo.log("Property not found"); return ''; })() :
53562             //    ......
53563             
53564             var udef_ar = [];
53565             var lookfor = '';
53566             Roo.each(name.split('.'), function(st) {
53567                 lookfor += (lookfor.length ? '.': '') + st;
53568                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
53569             });
53570             
53571             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
53572             
53573             
53574             if(format && useF){
53575                 
53576                 args = args ? ',' + args : "";
53577                  
53578                 if(format.substr(0, 5) != "this."){
53579                     format = "fm." + format + '(';
53580                 }else{
53581                     format = 'this.call("'+ format.substr(5) + '", ';
53582                     args = ", values";
53583                 }
53584                 
53585                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
53586             }
53587              
53588             if (args.length) {
53589                 // called with xxyx.yuu:(test,test)
53590                 // change to ()
53591                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
53592             }
53593             // raw.. - :raw modifier..
53594             return "'"+ sep + udef_st  + name + ")"+sep+"'";
53595             
53596         };
53597         var body;
53598         // branched to use + in gecko and [].join() in others
53599         if(Roo.isGecko){
53600             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
53601                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
53602                     "';};};";
53603         }else{
53604             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
53605             body.push(tpl.body.replace(/(\r\n|\n)/g,
53606                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
53607             body.push("'].join('');};};");
53608             body = body.join('');
53609         }
53610         
53611         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
53612        
53613         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
53614         eval(body);
53615         
53616         return this;
53617     },
53618
53619     applyTemplate : function(values){
53620         return this.master.compiled.call(this, values, {});
53621         //var s = this.subs;
53622     },
53623
53624     apply : function(){
53625         return this.applyTemplate.apply(this, arguments);
53626     }
53627
53628  });
53629
53630 Roo.XTemplate.from = function(el){
53631     el = Roo.getDom(el);
53632     return new Roo.XTemplate(el.value || el.innerHTML);
53633 };/*
53634  * Original code for Roojs - LGPL
53635  * <script type="text/javascript">
53636  */
53637  
53638 /**
53639  * @class Roo.XComponent
53640  * A delayed Element creator...
53641  * Or a way to group chunks of interface together.
53642  * 
53643  * Mypart.xyx = new Roo.XComponent({
53644
53645     parent : 'Mypart.xyz', // empty == document.element.!!
53646     order : '001',
53647     name : 'xxxx'
53648     region : 'xxxx'
53649     disabled : function() {} 
53650      
53651     tree : function() { // return an tree of xtype declared components
53652         var MODULE = this;
53653         return 
53654         {
53655             xtype : 'NestedLayoutPanel',
53656             // technicall
53657         }
53658      ]
53659  *})
53660  *
53661  *
53662  * It can be used to build a big heiracy, with parent etc.
53663  * or you can just use this to render a single compoent to a dom element
53664  * MYPART.render(Roo.Element | String(id) | dom_element )
53665  * 
53666  * @extends Roo.util.Observable
53667  * @constructor
53668  * @param cfg {Object} configuration of component
53669  * 
53670  */
53671 Roo.XComponent = function(cfg) {
53672     Roo.apply(this, cfg);
53673     this.addEvents({ 
53674         /**
53675              * @event built
53676              * Fires when this the componnt is built
53677              * @param {Roo.XComponent} c the component
53678              */
53679         'built' : true
53680         
53681     });
53682     this.region = this.region || 'center'; // default..
53683     Roo.XComponent.register(this);
53684     this.modules = false;
53685     this.el = false; // where the layout goes..
53686     
53687     
53688 }
53689 Roo.extend(Roo.XComponent, Roo.util.Observable, {
53690     /**
53691      * @property el
53692      * The created element (with Roo.factory())
53693      * @type {Roo.Layout}
53694      */
53695     el  : false,
53696     
53697     /**
53698      * @property el
53699      * for BC  - use el in new code
53700      * @type {Roo.Layout}
53701      */
53702     panel : false,
53703     
53704     /**
53705      * @property layout
53706      * for BC  - use el in new code
53707      * @type {Roo.Layout}
53708      */
53709     layout : false,
53710     
53711      /**
53712      * @cfg {Function|boolean} disabled
53713      * If this module is disabled by some rule, return true from the funtion
53714      */
53715     disabled : false,
53716     
53717     /**
53718      * @cfg {String} parent 
53719      * Name of parent element which it get xtype added to..
53720      */
53721     parent: false,
53722     
53723     /**
53724      * @cfg {String} order
53725      * Used to set the order in which elements are created (usefull for multiple tabs)
53726      */
53727     
53728     order : false,
53729     /**
53730      * @cfg {String} name
53731      * String to display while loading.
53732      */
53733     name : false,
53734     /**
53735      * @cfg {String} region
53736      * Region to render component to (defaults to center)
53737      */
53738     region : 'center',
53739     
53740     /**
53741      * @cfg {Array} items
53742      * A single item array - the first element is the root of the tree..
53743      * It's done this way to stay compatible with the Xtype system...
53744      */
53745     items : false,
53746     
53747     /**
53748      * @property _tree
53749      * The method that retuns the tree of parts that make up this compoennt 
53750      * @type {function}
53751      */
53752     _tree  : false,
53753     
53754      /**
53755      * render
53756      * render element to dom or tree
53757      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
53758      */
53759     
53760     render : function(el)
53761     {
53762         
53763         el = el || false;
53764         var hp = this.parent ? 1 : 0;
53765         
53766         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
53767             // if parent is a '#.....' string, then let's use that..
53768             var ename = this.parent.substr(1)
53769             this.parent = false;
53770             el = Roo.get(ename);
53771             if (!el) {
53772                 Roo.log("Warning - element can not be found :#" + ename );
53773                 return;
53774             }
53775         }
53776         
53777         
53778         if (!this.parent) {
53779             
53780             el = el ? Roo.get(el) : false;      
53781             
53782             // it's a top level one..
53783             this.parent =  {
53784                 el : new Roo.BorderLayout(el || document.body, {
53785                 
53786                      center: {
53787                          titlebar: false,
53788                          autoScroll:false,
53789                          closeOnTab: true,
53790                          tabPosition: 'top',
53791                           //resizeTabs: true,
53792                          alwaysShowTabs: el && hp? false :  true,
53793                          hideTabs: el || !hp ? true :  false,
53794                          minTabWidth: 140
53795                      }
53796                  })
53797             }
53798         }
53799         
53800                 if (!this.parent.el) {
53801                         // probably an old style ctor, which has been disabled.
53802                         return;
53803                         
53804                 }
53805                 // The 'tree' method is  '_tree now' 
53806             
53807         var tree = this._tree ? this._tree() : this.tree();
53808         tree.region = tree.region || this.region;
53809         this.el = this.parent.el.addxtype(tree);
53810         this.fireEvent('built', this);
53811         
53812         this.panel = this.el;
53813         this.layout = this.panel.layout;
53814                 this.parentLayout = this.parent.layout  || false;  
53815          
53816     }
53817     
53818 });
53819
53820 Roo.apply(Roo.XComponent, {
53821     /**
53822      * @property  hideProgress
53823      * true to disable the building progress bar.. usefull on single page renders.
53824      * @type Boolean
53825      */
53826     hideProgress : false,
53827     /**
53828      * @property  buildCompleted
53829      * True when the builder has completed building the interface.
53830      * @type Boolean
53831      */
53832     buildCompleted : false,
53833      
53834     /**
53835      * @property  topModule
53836      * the upper most module - uses document.element as it's constructor.
53837      * @type Object
53838      */
53839      
53840     topModule  : false,
53841       
53842     /**
53843      * @property  modules
53844      * array of modules to be created by registration system.
53845      * @type {Array} of Roo.XComponent
53846      */
53847     
53848     modules : [],
53849     /**
53850      * @property  elmodules
53851      * array of modules to be created by which use #ID 
53852      * @type {Array} of Roo.XComponent
53853      */
53854      
53855     elmodules : [],
53856
53857     
53858     /**
53859      * Register components to be built later.
53860      *
53861      * This solves the following issues
53862      * - Building is not done on page load, but after an authentication process has occured.
53863      * - Interface elements are registered on page load
53864      * - Parent Interface elements may not be loaded before child, so this handles that..
53865      * 
53866      *
53867      * example:
53868      * 
53869      * MyApp.register({
53870           order : '000001',
53871           module : 'Pman.Tab.projectMgr',
53872           region : 'center',
53873           parent : 'Pman.layout',
53874           disabled : false,  // or use a function..
53875         })
53876      
53877      * * @param {Object} details about module
53878      */
53879     register : function(obj) {
53880                 
53881         Roo.XComponent.event.fireEvent('register', obj);
53882         switch(typeof(obj.disabled) ) {
53883                 
53884             case 'undefined':
53885                 break;
53886             
53887             case 'function':
53888                 if ( obj.disabled() ) {
53889                         return;
53890                 }
53891                 break;
53892             
53893             default:
53894                 if (obj.disabled) {
53895                         return;
53896                 }
53897                 break;
53898         }
53899                 
53900         this.modules.push(obj);
53901          
53902     },
53903     /**
53904      * convert a string to an object..
53905      * eg. 'AAA.BBB' -> finds AAA.BBB
53906
53907      */
53908     
53909     toObject : function(str)
53910     {
53911         if (!str || typeof(str) == 'object') {
53912             return str;
53913         }
53914         if (str.substring(0,1) == '#') {
53915             return str;
53916         }
53917
53918         var ar = str.split('.');
53919         var rt, o;
53920         rt = ar.shift();
53921             /** eval:var:o */
53922         try {
53923             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
53924         } catch (e) {
53925             throw "Module not found : " + str;
53926         }
53927         
53928         if (o === false) {
53929             throw "Module not found : " + str;
53930         }
53931         Roo.each(ar, function(e) {
53932             if (typeof(o[e]) == 'undefined') {
53933                 throw "Module not found : " + str;
53934             }
53935             o = o[e];
53936         });
53937         
53938         return o;
53939         
53940     },
53941     
53942     
53943     /**
53944      * move modules into their correct place in the tree..
53945      * 
53946      */
53947     preBuild : function ()
53948     {
53949         var _t = this;
53950         Roo.each(this.modules , function (obj)
53951         {
53952             Roo.XComponent.event.fireEvent('beforebuild', obj);
53953             
53954             var opar = obj.parent;
53955             try { 
53956                 obj.parent = this.toObject(opar);
53957             } catch(e) {
53958                 Roo.log("parent:toObject failed: " + e.toString());
53959                 return;
53960             }
53961             
53962             if (!obj.parent) {
53963                 Roo.debug && Roo.log("GOT top level module");
53964                 Roo.debug && Roo.log(obj);
53965                 obj.modules = new Roo.util.MixedCollection(false, 
53966                     function(o) { return o.order + '' }
53967                 );
53968                 this.topModule = obj;
53969                 return;
53970             }
53971                         // parent is a string (usually a dom element name..)
53972             if (typeof(obj.parent) == 'string') {
53973                 this.elmodules.push(obj);
53974                 return;
53975             }
53976             if (obj.parent.constructor != Roo.XComponent) {
53977                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
53978             }
53979             if (!obj.parent.modules) {
53980                 obj.parent.modules = new Roo.util.MixedCollection(false, 
53981                     function(o) { return o.order + '' }
53982                 );
53983             }
53984             if (obj.parent.disabled) {
53985                 obj.disabled = true;
53986             }
53987             obj.parent.modules.add(obj);
53988         }, this);
53989     },
53990     
53991      /**
53992      * make a list of modules to build.
53993      * @return {Array} list of modules. 
53994      */ 
53995     
53996     buildOrder : function()
53997     {
53998         var _this = this;
53999         var cmp = function(a,b) {   
54000             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
54001         };
54002         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
54003             throw "No top level modules to build";
54004         }
54005         
54006         // make a flat list in order of modules to build.
54007         var mods = this.topModule ? [ this.topModule ] : [];
54008                 
54009         // elmodules (is a list of DOM based modules )
54010         Roo.each(this.elmodules, function(e) {
54011             mods.push(e)
54012         });
54013
54014         
54015         // add modules to their parents..
54016         var addMod = function(m) {
54017             Roo.debug && Roo.log("build Order: add: " + m.name);
54018             
54019         mods.push(m);
54020         if (m.modules && !m.disabled) {
54021             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
54022             m.modules.keySort('ASC',  cmp );
54023             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
54024
54025             m.modules.each(addMod);
54026         } else {
54027             Roo.debug && Roo.log("build Order: no child modules");
54028             }
54029             // not sure if this is used any more..
54030             if (m.finalize) {
54031                 m.finalize.name = m.name + " (clean up) ";
54032                 mods.push(m.finalize);
54033             }
54034             
54035         }
54036         if (this.topModule) { 
54037             this.topModule.modules.keySort('ASC',  cmp );
54038             this.topModule.modules.each(addMod);
54039         }
54040         return mods;
54041     },
54042     
54043      /**
54044      * Build the registered modules.
54045      * @param {Object} parent element.
54046      * @param {Function} optional method to call after module has been added.
54047      * 
54048      */ 
54049    
54050     build : function() 
54051     {
54052         
54053         this.preBuild();
54054         var mods = this.buildOrder();
54055       
54056         //this.allmods = mods;
54057         //Roo.debug && Roo.log(mods);
54058         //return;
54059         if (!mods.length) { // should not happen
54060             throw "NO modules!!!";
54061         }
54062         
54063         
54064         var msg = "Building Interface...";
54065         // flash it up as modal - so we store the mask!?
54066         if (!this.hideProgress) {
54067             Roo.MessageBox.show({ title: 'loading' });
54068             Roo.MessageBox.show({
54069                title: "Please wait...",
54070                msg: msg,
54071                width:450,
54072                progress:true,
54073                closable:false,
54074                modal: false
54075               
54076             });
54077         }
54078         var total = mods.length;
54079         
54080         var _this = this;
54081         var progressRun = function() {
54082             if (!mods.length) {
54083                 Roo.debug && Roo.log('hide?');
54084                 if (!this.hideProgress) {
54085                     Roo.MessageBox.hide();
54086                 }
54087                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
54088                 
54089                 // THE END...
54090                 return false;   
54091             }
54092             
54093             var m = mods.shift();
54094             
54095             
54096             Roo.debug && Roo.log(m);
54097             // not sure if this is supported any more.. - modules that are are just function
54098             if (typeof(m) == 'function') { 
54099                 m.call(this);
54100                 return progressRun.defer(10, _this);
54101             } 
54102             
54103             
54104             msg = "Building Interface " + (total  - mods.length) + 
54105                     " of " + total + 
54106                     (m.name ? (' - ' + m.name) : '');
54107                         Roo.debug && Roo.log(msg);
54108             if (!this.hideProgress) { 
54109                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
54110             }
54111             
54112          
54113             // is the module disabled?
54114             var disabled = (typeof(m.disabled) == 'function') ?
54115                 m.disabled.call(m.module.disabled) : m.disabled;    
54116             
54117             
54118             if (disabled) {
54119                 return progressRun(); // we do not update the display!
54120             }
54121             
54122             // now build 
54123             
54124                         
54125                         
54126             m.render();
54127             // it's 10 on top level, and 1 on others??? why...
54128             return progressRun.defer(10, _this);
54129              
54130         }
54131         progressRun.defer(1, _this);
54132      
54133         
54134         
54135     },
54136         
54137         
54138         /**
54139          * Event Object.
54140          *
54141          *
54142          */
54143         event: false, 
54144     /**
54145          * wrapper for event.on - aliased later..  
54146          * Typically use to register a event handler for register:
54147          *
54148          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
54149          *
54150          */
54151     on : false
54152    
54153     
54154     
54155 });
54156
54157 Roo.XComponent.event = new Roo.util.Observable({
54158                 events : { 
54159                         /**
54160                          * @event register
54161                          * Fires when an Component is registered,
54162                          * set the disable property on the Component to stop registration.
54163                          * @param {Roo.XComponent} c the component being registerd.
54164                          * 
54165                          */
54166                         'register' : true,
54167             /**
54168                          * @event beforebuild
54169                          * Fires before each Component is built
54170                          * can be used to apply permissions.
54171                          * @param {Roo.XComponent} c the component being registerd.
54172                          * 
54173                          */
54174                         'beforebuild' : true,
54175                         /**
54176                          * @event buildcomplete
54177                          * Fires on the top level element when all elements have been built
54178                          * @param {Roo.XComponent} the top level component.
54179                          */
54180                         'buildcomplete' : true
54181                         
54182                 }
54183 });
54184
54185 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
54186  //<script type="text/javascript">
54187
54188
54189 /**
54190  * @class Roo.Login
54191  * @extends Roo.LayoutDialog
54192  * A generic Login Dialog..... - only one needed in theory!?!?
54193  *
54194  * Fires XComponent builder on success...
54195  * 
54196  * Sends 
54197  *    username,password, lang = for login actions.
54198  *    check = 1 for periodic checking that sesion is valid.
54199  *    passwordRequest = email request password
54200  *    logout = 1 = to logout
54201  * 
54202  * Affects: (this id="????" elements)
54203  *   loading  (removed) (used to indicate application is loading)
54204  *   loading-mask (hides) (used to hide application when it's building loading)
54205  *   
54206  * 
54207  * Usage: 
54208  *    
54209  * 
54210  * Myapp.login = Roo.Login({
54211      url: xxxx,
54212    
54213      realm : 'Myapp', 
54214      
54215      
54216      method : 'POST',
54217      
54218      
54219      * 
54220  })
54221  * 
54222  * 
54223  * 
54224  **/
54225  
54226 Roo.Login = function(cfg)
54227 {
54228     this.addEvents({
54229         'refreshed' : true
54230     });
54231     
54232     Roo.apply(this,cfg);
54233     
54234     Roo.onReady(function() {
54235         this.onLoad();
54236     }, this);
54237     // call parent..
54238     
54239    
54240     Roo.Login.superclass.constructor.call(this, this);
54241     //this.addxtype(this.items[0]);
54242     
54243     
54244 }
54245
54246
54247 Roo.extend(Roo.Login, Roo.LayoutDialog, {
54248     
54249     /**
54250      * @cfg {String} method
54251      * Method used to query for login details.
54252      */
54253     
54254     method : 'POST',
54255     /**
54256      * @cfg {String} url
54257      * URL to query login data. - eg. baseURL + '/Login.php'
54258      */
54259     url : '',
54260     
54261     /**
54262      * @property user
54263      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
54264      * @type {Object} 
54265      */
54266     user : false,
54267     /**
54268      * @property checkFails
54269      * Number of times we have attempted to get authentication check, and failed.
54270      * @type {Number} 
54271      */
54272     checkFails : 0,
54273       /**
54274      * @property intervalID
54275      * The window interval that does the constant login checking.
54276      * @type {Number} 
54277      */
54278     intervalID : 0,
54279     
54280     
54281     onLoad : function() // called on page load...
54282     {
54283         // load 
54284          
54285         if (Roo.get('loading')) { // clear any loading indicator..
54286             Roo.get('loading').remove();
54287         }
54288         
54289         //this.switchLang('en'); // set the language to english..
54290        
54291         this.check({
54292             success:  function(response, opts)  {  // check successfull...
54293             
54294                 var res = this.processResponse(response);
54295                 this.checkFails =0;
54296                 if (!res.success) { // error!
54297                     this.checkFails = 5;
54298                     //console.log('call failure');
54299                     return this.failure(response,opts);
54300                 }
54301                 
54302                 if (!res.data.id) { // id=0 == login failure.
54303                     return this.show();
54304                 }
54305                 
54306                               
54307                         //console.log(success);
54308                 this.fillAuth(res.data);   
54309                 this.checkFails =0;
54310                 Roo.XComponent.build();
54311             },
54312             failure : this.show
54313         });
54314         
54315     }, 
54316     
54317     
54318     check: function(cfg) // called every so often to refresh cookie etc..
54319     {
54320         if (cfg.again) { // could be undefined..
54321             this.checkFails++;
54322         } else {
54323             this.checkFails = 0;
54324         }
54325         var _this = this;
54326         if (this.sending) {
54327             if ( this.checkFails > 4) {
54328                 Roo.MessageBox.alert("Error",  
54329                     "Error getting authentication status. - try reloading, or wait a while", function() {
54330                         _this.sending = false;
54331                     }); 
54332                 return;
54333             }
54334             cfg.again = true;
54335             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
54336             return;
54337         }
54338         this.sending = true;
54339         
54340         Roo.Ajax.request({  
54341             url: this.url,
54342             params: {
54343                 getAuthUser: true
54344             },  
54345             method: this.method,
54346             success:  cfg.success || this.success,
54347             failure : cfg.failure || this.failure,
54348             scope : this,
54349             callCfg : cfg
54350               
54351         });  
54352     }, 
54353     
54354     
54355     logout: function()
54356     {
54357         window.onbeforeunload = function() { }; // false does not work for IE..
54358         this.user = false;
54359         var _this = this;
54360         
54361         Roo.Ajax.request({  
54362             url: this.url,
54363             params: {
54364                 logout: 1
54365             },  
54366             method: 'GET',
54367             failure : function() {
54368                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
54369                     document.location = document.location.toString() + '?ts=' + Math.random();
54370                 });
54371                 
54372             },
54373             success : function() {
54374                 _this.user = false;
54375                 this.checkFails =0;
54376                 // fixme..
54377                 document.location = document.location.toString() + '?ts=' + Math.random();
54378             }
54379               
54380               
54381         }); 
54382     },
54383     
54384     processResponse : function (response)
54385     {
54386         var res = '';
54387         try {
54388             res = Roo.decode(response.responseText);
54389             // oops...
54390             if (typeof(res) != 'object') {
54391                 res = { success : false, errorMsg : res, errors : true };
54392             }
54393             if (typeof(res.success) == 'undefined') {
54394                 res.success = false;
54395             }
54396             
54397         } catch(e) {
54398             res = { success : false,  errorMsg : response.responseText, errors : true };
54399         }
54400         return res;
54401     },
54402     
54403     success : function(response, opts)  // check successfull...
54404     {  
54405         this.sending = false;
54406         var res = this.processResponse(response);
54407         if (!res.success) {
54408             return this.failure(response, opts);
54409         }
54410         if (!res.data || !res.data.id) {
54411             return this.failure(response,opts);
54412         }
54413         //console.log(res);
54414         this.fillAuth(res.data);
54415         
54416         this.checkFails =0;
54417         
54418     },
54419     
54420     
54421     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
54422     {
54423         this.authUser = -1;
54424         this.sending = false;
54425         var res = this.processResponse(response);
54426         //console.log(res);
54427         if ( this.checkFails > 2) {
54428         
54429             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
54430                 "Error getting authentication status. - try reloading"); 
54431             return;
54432         }
54433         opts.callCfg.again = true;
54434         this.check.defer(1000, this, [ opts.callCfg ]);
54435         return;  
54436     },
54437     
54438     
54439     
54440     fillAuth: function(au) {
54441         this.startAuthCheck();
54442         this.authUserId = au.id;
54443         this.authUser = au;
54444         this.lastChecked = new Date();
54445         this.fireEvent('refreshed', au);
54446         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
54447         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
54448         au.lang = au.lang || 'en';
54449         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
54450         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
54451         this.switchLang(au.lang );
54452         
54453      
54454         // open system... - -on setyp..
54455         if (this.authUserId  < 0) {
54456             Roo.MessageBox.alert("Warning", 
54457                 "This is an open system - please set up a admin user with a password.");  
54458         }
54459          
54460         //Pman.onload(); // which should do nothing if it's a re-auth result...
54461         
54462              
54463     },
54464     
54465     startAuthCheck : function() // starter for timeout checking..
54466     {
54467         if (this.intervalID) { // timer already in place...
54468             return false;
54469         }
54470         var _this = this;
54471         this.intervalID =  window.setInterval(function() {
54472               _this.check(false);
54473             }, 120000); // every 120 secs = 2mins..
54474         
54475         
54476     },
54477          
54478     
54479     switchLang : function (lang) 
54480     {
54481         _T = typeof(_T) == 'undefined' ? false : _T;
54482           if (!_T || !lang.length) {
54483             return;
54484         }
54485         
54486         if (!_T && lang != 'en') {
54487             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
54488             return;
54489         }
54490         
54491         if (typeof(_T.en) == 'undefined') {
54492             _T.en = {};
54493             Roo.apply(_T.en, _T);
54494         }
54495         
54496         if (typeof(_T[lang]) == 'undefined') {
54497             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
54498             return;
54499         }
54500         
54501         
54502         Roo.apply(_T, _T[lang]);
54503         // just need to set the text values for everything...
54504         var _this = this;
54505         /* this will not work ...
54506         if (this.form) { 
54507             
54508                
54509             function formLabel(name, val) {
54510                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
54511             }
54512             
54513             formLabel('password', "Password"+':');
54514             formLabel('username', "Email Address"+':');
54515             formLabel('lang', "Language"+':');
54516             this.dialog.setTitle("Login");
54517             this.dialog.buttons[0].setText("Forgot Password");
54518             this.dialog.buttons[1].setText("Login");
54519         }
54520         */
54521         
54522         
54523     },
54524     
54525     
54526     title: "Login",
54527     modal: true,
54528     width:  350,
54529     //height: 230,
54530     height: 180,
54531     shadow: true,
54532     minWidth:200,
54533     minHeight:180,
54534     //proxyDrag: true,
54535     closable: false,
54536     draggable: false,
54537     collapsible: false,
54538     resizable: false,
54539     center: {  // needed??
54540         autoScroll:false,
54541         titlebar: false,
54542        // tabPosition: 'top',
54543         hideTabs: true,
54544         closeOnTab: true,
54545         alwaysShowTabs: false
54546     } ,
54547     listeners : {
54548         
54549         show  : function(dlg)
54550         {
54551             //console.log(this);
54552             this.form = this.layout.getRegion('center').activePanel.form;
54553             this.form.dialog = dlg;
54554             this.buttons[0].form = this.form;
54555             this.buttons[0].dialog = dlg;
54556             this.buttons[1].form = this.form;
54557             this.buttons[1].dialog = dlg;
54558            
54559            //this.resizeToLogo.defer(1000,this);
54560             // this is all related to resizing for logos..
54561             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
54562            //// if (!sz) {
54563              //   this.resizeToLogo.defer(1000,this);
54564              //   return;
54565            // }
54566             //var w = Ext.lib.Dom.getViewWidth() - 100;
54567             //var h = Ext.lib.Dom.getViewHeight() - 100;
54568             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
54569             //this.center();
54570             if (this.disabled) {
54571                 this.hide();
54572                 return;
54573             }
54574             
54575             if (this.user.id < 0) { // used for inital setup situations.
54576                 return;
54577             }
54578             
54579             if (this.intervalID) {
54580                 // remove the timer
54581                 window.clearInterval(this.intervalID);
54582                 this.intervalID = false;
54583             }
54584             
54585             
54586             if (Roo.get('loading')) {
54587                 Roo.get('loading').remove();
54588             }
54589             if (Roo.get('loading-mask')) {
54590                 Roo.get('loading-mask').hide();
54591             }
54592             
54593             //incomming._node = tnode;
54594             this.form.reset();
54595             //this.dialog.modal = !modal;
54596             //this.dialog.show();
54597             this.el.unmask(); 
54598             
54599             
54600             this.form.setValues({
54601                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
54602                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
54603             });
54604             
54605             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
54606             if (this.form.findField('username').getValue().length > 0 ){
54607                 this.form.findField('password').focus();
54608             } else {
54609                this.form.findField('username').focus();
54610             }
54611     
54612         }
54613     },
54614     items : [
54615          {
54616        
54617             xtype : 'ContentPanel',
54618             xns : Roo,
54619             region: 'center',
54620             fitToFrame : true,
54621             
54622             items : [
54623     
54624                 {
54625                
54626                     xtype : 'Form',
54627                     xns : Roo.form,
54628                     labelWidth: 100,
54629                     style : 'margin: 10px;',
54630                     
54631                     listeners : {
54632                         actionfailed : function(f, act) {
54633                             // form can return { errors: .... }
54634                                 
54635                             //act.result.errors // invalid form element list...
54636                             //act.result.errorMsg// invalid form element list...
54637                             
54638                             this.dialog.el.unmask();
54639                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
54640                                         "Login failed - communication error - try again.");
54641                                       
54642                         },
54643                         actioncomplete: function(re, act) {
54644                              
54645                             Roo.state.Manager.set(
54646                                 this.dialog.realm + '.username',  
54647                                     this.findField('username').getValue()
54648                             );
54649                             Roo.state.Manager.set(
54650                                 this.dialog.realm + '.lang',  
54651                                 this.findField('lang').getValue() 
54652                             );
54653                             
54654                             this.dialog.fillAuth(act.result.data);
54655                               
54656                             this.dialog.hide();
54657                             
54658                             if (Roo.get('loading-mask')) {
54659                                 Roo.get('loading-mask').show();
54660                             }
54661                             Roo.XComponent.build();
54662                             
54663                              
54664                             
54665                         }
54666                     },
54667                     items : [
54668                         {
54669                             xtype : 'TextField',
54670                             xns : Roo.form,
54671                             fieldLabel: "Email Address",
54672                             name: 'username',
54673                             width:200,
54674                             autoCreate : {tag: "input", type: "text", size: "20"}
54675                         },
54676                         {
54677                             xtype : 'TextField',
54678                             xns : Roo.form,
54679                             fieldLabel: "Password",
54680                             inputType: 'password',
54681                             name: 'password',
54682                             width:200,
54683                             autoCreate : {tag: "input", type: "text", size: "20"},
54684                             listeners : {
54685                                 specialkey : function(e,ev) {
54686                                     if (ev.keyCode == 13) {
54687                                         this.form.dialog.el.mask("Logging in");
54688                                         this.form.doAction('submit', {
54689                                             url: this.form.dialog.url,
54690                                             method: this.form.dialog.method
54691                                         });
54692                                     }
54693                                 }
54694                             }  
54695                         },
54696                         {
54697                             xtype : 'ComboBox',
54698                             xns : Roo.form,
54699                             fieldLabel: "Language",
54700                             name : 'langdisp',
54701                             store: {
54702                                 xtype : 'SimpleStore',
54703                                 fields: ['lang', 'ldisp'],
54704                                 data : [
54705                                     [ 'en', 'English' ],
54706                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
54707                                     [ 'zh_CN', '\u7C21\u4E2D' ]
54708                                 ]
54709                             },
54710                             
54711                             valueField : 'lang',
54712                             hiddenName:  'lang',
54713                             width: 200,
54714                             displayField:'ldisp',
54715                             typeAhead: false,
54716                             editable: false,
54717                             mode: 'local',
54718                             triggerAction: 'all',
54719                             emptyText:'Select a Language...',
54720                             selectOnFocus:true,
54721                             listeners : {
54722                                 select :  function(cb, rec, ix) {
54723                                     this.form.switchLang(rec.data.lang);
54724                                 }
54725                             }
54726                         
54727                         }
54728                     ]
54729                 }
54730                   
54731                 
54732             ]
54733         }
54734     ],
54735     buttons : [
54736         {
54737             xtype : 'Button',
54738             xns : 'Roo',
54739             text : "Forgot Password",
54740             listeners : {
54741                 click : function() {
54742                     //console.log(this);
54743                     var n = this.form.findField('username').getValue();
54744                     if (!n.length) {
54745                         Roo.MessageBox.alert("Error", "Fill in your email address");
54746                         return;
54747                     }
54748                     Roo.Ajax.request({
54749                         url: this.dialog.url,
54750                         params: {
54751                             passwordRequest: n
54752                         },
54753                         method: this.dialog.method,
54754                         success:  function(response, opts)  {  // check successfull...
54755                         
54756                             var res = this.dialog.processResponse(response);
54757                             if (!res.success) { // error!
54758                                Roo.MessageBox.alert("Error" ,
54759                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
54760                                return;
54761                             }
54762                             Roo.MessageBox.alert("Notice" ,
54763                                 "Please check you email for the Password Reset message");
54764                         },
54765                         failure : function() {
54766                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
54767                         }
54768                         
54769                     });
54770                 }
54771             }
54772         },
54773         {
54774             xtype : 'Button',
54775             xns : 'Roo',
54776             text : "Login",
54777             listeners : {
54778                 
54779                 click : function () {
54780                         
54781                     this.dialog.el.mask("Logging in");
54782                     this.form.doAction('submit', {
54783                             url: this.dialog.url,
54784                             method: this.dialog.method
54785                     });
54786                 }
54787             }
54788         }
54789     ]
54790   
54791   
54792 })
54793  
54794
54795
54796